diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 3e6bdbf1969..71c1d787cf1 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,6 +1,9 @@ --- name: Bug report about: Create a report to help us improve +title: '' +labels: '' +assignees: '' --- diff --git a/.github/ISSUE_TEMPLATE/design-request.md b/.github/ISSUE_TEMPLATE/design-request.md new file mode 100644 index 00000000000..835ceca25e9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/design-request.md @@ -0,0 +1,26 @@ +--- +name: Design Request +about: 'Describe a design or art need ' +title: '' +labels: design +assignees: soniakandah, unthinkmedia, BeckHaru + +--- + +Describe your design or art need by answering the questions below: + +## What are the requirements for the project? + +## Is there a deadline or date this is needed by? + +## Please share any existing references, screenshots, links, etc. here that will help us better understand your requirements + +## Optional Additional Questions: + +### Who are the stakeholders that need to sign off on this design? Tag them here. + +### What audience is this request for? + +### What does success look like? + +### How will we measure if this is successful or not? diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 066b2d920a2..bbcbbe7d615 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,6 +1,9 @@ --- name: Feature request about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' --- diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..2c48305b7eb --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,11 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + groups: + github-actions: + patterns: ["*"] + schedule: + interval: "weekly" + cooldown: + default-days: 7 diff --git a/.github/workflows/check-if-merged-pr.yml b/.github/workflows/check-if-merged-pr.yml new file mode 100644 index 00000000000..58b594c83a0 --- /dev/null +++ b/.github/workflows/check-if-merged-pr.yml @@ -0,0 +1,54 @@ +name: Check if the commit is part of a merged PR + +on: + workflow_call: + outputs: + is_merged_pr: + description: "Whether the current push came from a merged PR" + value: ${{ jobs.check-pr.outputs.is_merged_pr }} + pr_head_sha: + description: "The head SHA of the merged PR" + value: ${{ jobs.check-pr.outputs.pr_head_sha }} + +jobs: + check-pr: + runs-on: ubuntu-latest + outputs: + is_merged_pr: ${{ steps.parse-check-pr.outputs.is_merged_pr }} + pr_head_sha: ${{ steps.parse-check-pr.outputs.pr_head_sha }} + steps: + - name: Check if this commit is from a merged PR + id: check-pr + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + result-encoding: string + script: | + const commitSha = context.sha; + const { data: prs } = await github.rest.repos.listPullRequestsAssociatedWithCommit({ + owner: context.repo.owner, + repo: context.repo.repo, + commit_sha: commitSha + }); + + if (!prs.length) { + core.info('No PRs associated with this commit.'); + return JSON.stringify({ is_merged_pr: false, pr_head_sha: '' }); + } + + const mergedPr = prs.find(pr => pr.merged_at !== null); + + if (!mergedPr) { + core.info('PRs found, but none were merged.'); + return JSON.stringify({ is_merged_pr: false, pr_head_sha: '' }); + } + + core.info(`Found merged PR head SHA: ${mergedPr.head.sha}`); + return JSON.stringify({ is_merged_pr: true, pr_head_sha: mergedPr.head.sha }); + + - name: Parse outputs + id: parse-check-pr + shell: bash + run: | + echo "Parsing result: ${{ steps.check-pr.outputs.result }}" + echo "is_merged_pr=$(jq -r '.is_merged_pr' <<< '${{ steps.check-pr.outputs.result }}')" >> $GITHUB_OUTPUT + echo "pr_head_sha=$(jq -r '.pr_head_sha' <<< '${{ steps.check-pr.outputs.result }}')" >> $GITHUB_OUTPUT diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 4b136025372..7e9218a6619 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -2,39 +2,48 @@ name: "Code scanning - action" on: push: + branches: [ 'master', 'stable*', 'v[0-9]*' ] pull_request: + # The branches below must be a subset of the branches above + branches: [ master ] schedule: - cron: '0 19 * * 0' + workflow_dispatch: jobs: - CodeQL-Build: + analyze: + name: Analyze + + strategy: + fail-fast: false + matrix: + include: + - language: javascript-typescript + build-mode: none + - language: cpp + build-mode: none # CodeQL runs on ubuntu-latest and windows-latest runs-on: ubuntu-latest + permissions: + security-events: write + # required to fetch internal or private CodeQL packs + packages: read steps: - name: Checkout repository - uses: actions/checkout@v2 - with: - # We must fetch at least the immediate parents so that if this is - # a pull request then we can checkout the head. - fetch-depth: 2 - - # If this run was triggered by a pull request event, then checkout - # the head of the pull request instead of the merge commit. - - run: git checkout HEAD^2 - if: ${{ github.event_name == 'pull_request' }} - + uses: actions/checkout@main + # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6 with: - languages: javascript - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below) - - name: Autobuild - uses: github/codeql-action/autobuild@v1 + languages: ${{ matrix.language }} + build-mode: ${{ matrix.build-mode }} + # # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). + # # If this step fails, then you should remove it and run the build manually (see below) + # - name: Autobuild + # uses: github/codeql-action/autobuild@v3 # â„šī¸ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl @@ -48,4 +57,6 @@ jobs: # make release - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@5595ccaf912efad79be6eef63a5619ff05969be3 # v4.37.6 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/is-vtag.yml b/.github/workflows/is-vtag.yml new file mode 100644 index 00000000000..720e9518bbe --- /dev/null +++ b/.github/workflows/is-vtag.yml @@ -0,0 +1,31 @@ +name: Whether the tag is a semver tag + +on: + workflow_call: + outputs: + is_vtag: + description: 'Whether the tag is a semver tag' + value: ${{ jobs.filter-vtags.outputs.is_vtag }} + +jobs: + filter-vtags: + runs-on: ubuntu-latest + outputs: + is_vtag: ${{ steps.check-tag.outputs.is_vtag }} + tag: ${{ steps.check-tag.outputs.tag }} + steps: + - name: Inputs + run: | + echo "GITHUB_REF_TYPE=${GITHUB_REF_TYPE}" + echo "GITHUB_REF_NAME=${GITHUB_REF_NAME}" + - name: Check tag pattern + id: check-tag + run: | + if [[ "${GITHUB_REF_TYPE}" == "tag" && "${GITHUB_REF_NAME}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "is_vtag=true" >> "$GITHUB_OUTPUT" + echo "tag=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT" + else + echo "is_vtag=false" >> "$GITHUB_OUTPUT" + fi + - name: Outputs + run: echo "Step output is_vtag = ${{ steps.check-tag.outputs.is_vtag }}" && echo "Step output tag = ${{ steps.check-tag.outputs.tag }}" diff --git a/.github/workflows/pxt-buildmain.yml b/.github/workflows/pxt-buildmain.yml deleted file mode 100644 index 60c501d271e..00000000000 --- a/.github/workflows/pxt-buildmain.yml +++ /dev/null @@ -1,40 +0,0 @@ -name: pxt-buildmain - -on: - push: - branches: - - 'master' - - 'main' - create: - -jobs: - build: - - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [8.x] - - steps: - - uses: actions/checkout@v1 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} - - name: npm install - run: | - sudo apt-get install xvfb - sudo npm install -g pxt - npm install - - name: pxt ci - run: | - pxt ci - env: - CROWDIN_KEY: ${{ secrets.CROWDIN_KEY }} - PXT_ACCESS_TOKEN: ${{ secrets.PXT_ACCESS_TOKEN }} - PXT_RELEASE_REPO: ${{ secrets.PXT_RELEASE_REPO }} - NPM_ACCESS_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }} - CHROME_BIN: chromium-browser - DISPLAY: :99.0 - CI: true \ No newline at end of file diff --git a/.github/workflows/pxt-buildpr.yml b/.github/workflows/pxt-buildpr.yml deleted file mode 100644 index 22907593a56..00000000000 --- a/.github/workflows/pxt-buildpr.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: pxt-buildpr - -on: [pull_request] - -jobs: - build: - - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [8.x] - - steps: - - uses: actions/checkout@v1 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} - - name: npm install - run: | - sudo apt-get install xvfb - sudo npm install -g pxt - npm install - - name: pxt ci - run: | - pxt ci - env: - CHROME_BIN: chromium-browser - DISPLAY: :99.0 - CI: true \ No newline at end of file diff --git a/.github/workflows/pxt-buildpush.yml b/.github/workflows/pxt-buildpush.yml index cf69997449d..a48c08a05fc 100644 --- a/.github/workflows/pxt-buildpush.yml +++ b/.github/workflows/pxt-buildpush.yml @@ -2,38 +2,128 @@ name: pxt-buildpush on: push: - # main/master has its own build that includes the crowdin key - branches-ignore: - - 'main' - - 'master' + branches: + - '**' # Run workflow when any branch is updated + tags: + - '*' # Run workflow when any new tag is pushed + pull_request: + branches: + - '**' # Run workflow for pull requests targeting any branch + merge_group: + branches: + - '**' # Run workflow for merge queue checks targeting any branch + types: + - checks_requested + +permissions: + contents: write + id-token: write # Required for OIDC jobs: - build: + filter-vtags: + uses: ./.github/workflows/is-vtag.yml + + tag-bump-commit: + uses: ./.github/workflows/tag-bump-commit.yml + needs: filter-vtags + if: fromJSON(needs.filter-vtags.outputs.is_vtag || 'false') == false + buildpush: + name: buildpush runs-on: ubuntu-latest + needs: tag-bump-commit + if: always() && fromJSON(needs.tag-bump-commit.outputs.did_tag || 'false') == false + steps: + - uses: actions/checkout@main + with: + fetch-depth: 0 + fetch-tags: true - strategy: - matrix: - node-version: [8.x] + - name: Use Node.js + uses: actions/setup-node@main + with: + node-version: 22.x + + - name: Update npm + run: npm install -g npm@11 + + - name: npm install + run: | + sudo apt-get install xvfb + sudo npm install -g pxt + npm install + - name: pxt ci (without publish capability) + run: | + pxt ci + env: + CHROME_BIN: chromium-browser + DISPLAY: :99.0 + CI: true + + buildvtag: + # This job is a duplicate of pxt-buildvtag.yml's workflow + name: buildvtag + runs-on: ubuntu-latest + needs: tag-bump-commit + if: always() && fromJSON(needs.tag-bump-commit.outputs.did_tag || 'false') == true steps: - - uses: actions/checkout@v1 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 + - uses: actions/checkout@main + with: + fetch-depth: 0 + fetch-tags: true + + - name: Use Node.js + uses: actions/setup-node@main with: - node-version: ${{ matrix.node-version }} + node-version: 22.x + + - name: Update npm + run: npm install -g npm@11 + - name: npm install run: | sudo apt-get install xvfb sudo npm install -g pxt npm install - - name: pxt ci + + - name: pxt ci (with publish capability) run: | - pxt ci + pxt ci --publish env: + CROWDIN_KEY: ${{ secrets.CROWDIN_KEY }} PXT_ACCESS_TOKEN: ${{ secrets.PXT_ACCESS_TOKEN }} - PXT_RELEASE_REPO: ${{ secrets.PXT_RELEASE_REPO }} - NPM_ACCESS_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }} + NPM_PUBLISH: true CHROME_BIN: chromium-browser DISPLAY: :99.0 - CI: true \ No newline at end of file + CI: true + PXT_SKIP_GIT_COMMIT: true + + - name: Clone release repo + uses: actions/checkout@main + with: + repository: ${{ secrets.BUILT_REPO_NAME }} + path: tmp/releases/release + fetch-depth: 3 + fetch-tags: true + ssh-key: ${{ secrets.BUILT_REPO_DEPLOY_KEY }} + + - name: Read release info + id: release_info + run: | + tag=$(cat tmp/releases/release.json | jq -r '.tag') + url=$(cat tmp/releases/release.json | jq -r '.url') + echo "tag=$tag" >> "$GITHUB_OUTPUT" + echo "url=$url" >> "$GITHUB_OUTPUT" + + - name: Push artifacts to release repo + run: | + cp -a tmp/releases/built/. tmp/releases/release + cd tmp/releases/release + git config user.name "github-actions" + git config user.email "github-actions@users.noreply.github.com" + git add . + git commit -m "Release ${{steps.release_info.outputs.tag}} from ${{steps.release_info.outputs.url}}" + git tag ${{steps.release_info.outputs.tag}} + git push + git push --tags diff --git a/.github/workflows/pxt-buildvtag.yml b/.github/workflows/pxt-buildvtag.yml new file mode 100644 index 00000000000..8f980db4cc1 --- /dev/null +++ b/.github/workflows/pxt-buildvtag.yml @@ -0,0 +1,45 @@ +name: pxt-buildvtag + +on: + push: + tags: + - 'v*' # Run workflow when any new semver-ish tag is pushed + +jobs: + filter-vtags: + uses: ./.github/workflows/is-vtag.yml + + buildvtag: + name: buildvtag + # Only run this job if the push is a version tag + needs: filter-vtags + if: fromJSON(needs.filter-vtags.outputs.is_vtag || 'false') == true + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@main + with: + fetch-depth: 0 + fetch-tags: true + + - name: Use Node.js + uses: actions/setup-node@main + with: + node-version: 18.x + + - name: npm install + run: | + sudo apt-get install xvfb + sudo npm install -g pxt + npm install + + - name: pxt ci (with publish capability) + run: | + pxt ci --publish + env: + CROWDIN_KEY: ${{ secrets.CROWDIN_KEY }} + PXT_ACCESS_TOKEN: ${{ secrets.PXT_ACCESS_TOKEN }} + PXT_RELEASE_REPO: ${{ secrets.PXT_RELEASE_REPO }} + NPM_ACCESS_TOKEN: ${{ secrets.NPM_ACCESS_TOKEN }} + CHROME_BIN: chromium-browser + DISPLAY: :99.0 + CI: true diff --git a/.github/workflows/tag-bump-commit.yml b/.github/workflows/tag-bump-commit.yml new file mode 100644 index 00000000000..6a105d0cea8 --- /dev/null +++ b/.github/workflows/tag-bump-commit.yml @@ -0,0 +1,91 @@ +name: Tag version on merged bump commit + +on: + workflow_call: + outputs: + did_tag: + description: 'Whether a tag was created' + value: ${{ jobs.return.outputs.did_tag }} + +jobs: + check-merge: + uses: ./.github/workflows/check-if-merged-pr.yml + + check-merge-outputs: + needs: check-merge + runs-on: ubuntu-latest + if: always() + steps: + - name: check-merge outputs + run: | + echo "is_merged_pr = '${{ needs.check-merge.outputs.is_merged_pr }}'" + echo "pr_head_sha = '${{ needs.check-merge.outputs.pr_head_sha }}'" + + tag-version: + needs: check-merge + if: fromJSON(needs.check-merge.outputs.is_merged_pr || 'false') == true + runs-on: ubuntu-latest + outputs: + did_tag: ${{ steps.tag-op.outputs.did_tag }} + tag: ${{ steps.tag-op.outputs.tag }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 0 + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Tag commit if it's a version bump + id: tag-op + shell: bash + run: | + set -euxo pipefail + + COMMIT_SHA="${{ github.sha }}" + echo "==> Current merge commit SHA: $COMMIT_SHA" + + echo "==> Fetching commit message..." + COMMIT_MSG=$(git log -1 --pretty=%s "$COMMIT_SHA") + echo "==> Commit message: '$COMMIT_MSG'" + + TAGGED=false + + # Check if commit matches bump pattern and PR# + if [[ "$COMMIT_MSG" =~ \[pxt-cli\]\ bump\ version\ to\ v([0-9]+\.[0-9]+\.[0-9]+)\ \(\#[0-9]+\) ]]; then + VERSION="v${BASH_REMATCH[1]}" + echo "==> Detected bump version: $VERSION" + + # Check if tag already exists + if git rev-parse "$VERSION" >/dev/null 2>&1; then + echo "::warning::Tag $VERSION already exists — skipping tagging." + else + echo "==> Tagging $COMMIT_SHA with $VERSION" + git tag "$VERSION" "$COMMIT_SHA" + git push origin "$VERSION" + echo "tag=$VERSION" >> "$GITHUB_OUTPUT" + TAGGED=true + fi + else + echo "==> No merged bump commit detected — skipping tag creation." + fi + + echo "==> did_tag=$TAGGED" + echo "did_tag=$TAGGED" >> "$GITHUB_OUTPUT" + + not-tag-version: + needs: check-merge + if: fromJSON(needs.check-merge.outputs.is_merged_pr || 'false') == false + runs-on: ubuntu-latest + outputs: + did_tag: false + steps: + - run: echo "No tag because not a PR merge." + + return: + runs-on: ubuntu-latest + needs: [tag-version, not-tag-version] + if: always() + outputs: + did_tag: ${{ needs.tag-version.outputs.did_tag || false }} + steps: + - run: echo "Returning did_tag = ${{ needs.tag-version.outputs.did_tag || false }}" + - run: echo "Returning tag = ${{ needs.tag-version.outputs.tag || '' }}" diff --git a/.github/workflows/testghpkgs.yml b/.github/workflows/testghpkgs.yml index ca4d29f2084..c283366b1d8 100644 --- a/.github/workflows/testghpkgs.yml +++ b/.github/workflows/testghpkgs.yml @@ -14,11 +14,11 @@ jobs: branch: [stable3.0] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: ref: ${{ matrix.branch }} - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 + uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version: ${{ matrix.node-version }} - name: npm install @@ -28,7 +28,7 @@ jobs: - name: pxt buildtarget run: pxt buildtarget - name: cache build output - uses: actions/cache@v1 + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 env: cache-name: cache-testghpkgs with: @@ -45,7 +45,7 @@ jobs: PXT_ACCESS_TOKEN: ${{ secrets.PXT_ACCESS_TOKEN }} GITHUB_TOKEN: ${{ secrets.TRAVIS_GITHUB_ACCESS_TOKEN }} - name: upload build log - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 if: ${{ always() }} with: name: logs-${{ matrix.branch }} diff --git a/.gitignore b/.gitignore index 4e6323eb468..50e4cd2fc9a 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ electron-out hexcache build crowdinstats.csv +.pxt/ *.user *.sw? diff --git a/.vscode/settings.json b/.vscode/settings.json index fc770a23ed3..6bdb0b31179 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,6 @@ // Place your settings in this file to overwrite default and user settings. { - "files.autoSave": "afterDelay", + "files.autoSave": "off", "files.watcherExclude": { "**/.git/objects/**": true, "**/built/**": true, @@ -8,13 +8,19 @@ "**/yotta_modules/**": true, "**/yotta_targets": true, "**/pxt_modules/**": true - }, + }, "search.exclude": { "**/node_modules": true, "**/yotta_modules/**": true, "**/yotta_targets": true, "**/pxt_modules/**": true - }, + }, + "files.associations": { + "*.blocks": "html", + "*.overrides": "less", + "*.variables": "less", + "*.jres": "json" + }, "tslint.enable": true, "tslint.rulesDirectory": "node_modules/tslint-microsoft-contrib", "typescript.tsdk": "./node_modules/typescript/lib" diff --git a/.vscode/tasks.json b/.vscode/tasks.json index c360192204e..bc1f2f0d7aa 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,20 +1,19 @@ { - "version": "0.1.0", + "version": "2.0.0", // Task runner is jake "command": "pxt", - // Need to be executed in shell / cmd - "isShellCommand": true, - "showOutput": "always", "tasks": [ { - // TS build command is local. - "taskName": "serve", - // Make this the default build command. - "isBuildCommand": true, - // Use the redefined Typescript output problem matcher. + "label": "serve", + "type": "shell", + "command": "pxt", + "args": [ + "serve" + ], "problemMatcher": [ "$tsc" - ] + ], + "group": "build" } ] } diff --git a/README.md b/README.md index 353e1e8c672..251715629ea 100644 --- a/README.md +++ b/README.md @@ -65,28 +65,33 @@ cd .. git clone https://github.com/microsoft/pxt-common-packages cd pxt-common-packages npm install +``` + +6. Link pxt-common-packages to pxt +``` +npm link ../pxt cd .. ``` -6. Clone this repository. + +7. Clone this repository. ``` git clone https://github.com/microsoft/pxt-microbit cd pxt-microbit ``` -7. Install the PXT command line (add `sudo` for Mac/Linux shells). +8. Install the PXT command line (add `sudo` for Mac/Linux shells). ``` npm install -g pxt ``` -8. Install the pxt-microbit dependencies. +9. Install the pxt-microbit dependencies. ``` npm install ``` -8. Link pxt-microbit back to base pxt repo (add `sudo` for Mac/Linux shells). +10. Link pxt-microbit back to base pxt repo (add `sudo` for Mac/Linux shells). This step is only required if you intend to make changes to pxt and/or pxt-common-packages repos. If all you want is serve a local Makecode, you can skip this step. ``` -pxt link ../pxt -pxt link ../pxt-common-packages +npm link ../pxt ../pxt-common-packages ``` Note the above command assumes the folder structure of ``` @@ -147,11 +152,14 @@ If you are also modifiying CODAL, consider running ``pxt clean`` to ensure the p * do `export PXT_FORCE_LOCAL=1 PXT_RUNTIME_DEV=1 PXT_ASMDEBUG=1`; you can add `PXT_NODOCKER=1`; `pxt help` has help on these * find project folder under `pxt-microbit/projects`, typically `pxt-microbit/projects/Untitled-42` * if you're going to modify `.cpp` files in PXT, replace `"core": "*"` in `pxt.json` with `"core": "file:../../libs/core"`; - similarly `"radio": "file:../../libs/radio"` + similarly `"radio": "file:../../libs/radio"` and `"microphone": "file:../../libs/microphone"` * you can edit `main.ts` to change the PXT side of the program; you can also edit it from the localhost editor; note that `Download` in the localhost editor will produce different binary than command line, as it builds in the cloud and uses tagged version of CODAL * in that folder run `pxt build` - this will clone codal somewhere under `built/` (depends on build engine and docker) +* there can be an issue with exporting the variables i.e. PXT_FORCE, so including them in the build command can help solve issues `sudo PXT_NODOCKER=1 PXT_ASMDEBUG=1 PXT_RUNTIME_DEV=1 PXT_DEBUG=1 PXT_FORCE_LOCAL=1 PXT_COMPILE_SWITCHES=csv---mbcodal pxt build` +* if the target is not building, delete files in `hexcache` found in `pxt-microbit/built/hexcache` to force local build +* the built hex can be found in `pxt-microbit/projects//built` named `binary.hex` * similarly, you can run `pxt deploy` (or just `pxt` which is the same) - it will build and copy to `MICROBIT` drive * assuming the build folder is under `built/codal`, go to `built/codal/libraries` and run `code *` * in git tab, checkout appropriate branches (they are all in detached head state to the way we tag releases) @@ -176,6 +184,8 @@ Make sure to pull changes from all repos regularly. More instructions are at htt ## Update playlists in markdown +To add a new playlist, add an entry in ``/playlists.json``, and regenerate the markdown (see paragraph below). You'll now have a new markdown gallery file listing the videos which you can reference in ``/targetconfig.json``. + Get a Google API key and store it in the ``GOOGLE_API_KEY`` environment variables (turn on data from the app). ``` diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000000..869fdfe2b24 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). + + diff --git a/compiler/tsconfig.json b/compiler/tsconfig.json index 9c48eef1c02..0211c64a858 100644 --- a/compiler/tsconfig.json +++ b/compiler/tsconfig.json @@ -1,12 +1,24 @@ { "compilerOptions": { - "target": "es5", + "target": "es2017", "noImplicitAny": true, "noImplicitReturns": true, "noImplicitThis": true, "declaration": true, - "out": "../built/compiler.js", + "moduleResolution": "node", + "isolatedModules": false, + "outFile": "../built/compiler.js", + "rootDir": ".", "newLine": "LF", - "sourceMap": false + "sourceMap": false, + "typeRoots": ["../node_modules/@types"], + "types": [], + "lib": [ + "dom", + "dom.iterable", + "scripthost", + "es2017", + "ES2018.Promise" + ] } -} \ No newline at end of file +} diff --git a/docfiles/apptrackingweb.html b/docfiles/apptrackingweb.html new file mode 100644 index 00000000000..645a3bc5c95 --- /dev/null +++ b/docfiles/apptrackingweb.html @@ -0,0 +1,43 @@ + + + + + + \ No newline at end of file diff --git a/docs/SUMMARY.md b/docs/SUMMARY.md index 6431d33422f..753493ffab0 100644 --- a/docs/SUMMARY.md +++ b/docs/SUMMARY.md @@ -189,7 +189,6 @@ * [forever](/reference/basic/forever) * [pause](/reference/basic/pause) * [show arrow](/reference/basic/show-arrow) - * [show animation](/reference/basic/show-animation) * [Input](/reference/input) * [on button pressed](/reference/input/on-button-pressed) * [on gesture](/reference/input/on-gesture) @@ -296,6 +295,8 @@ * [on data received](/reference/serial/on-data-received) * [redirect](/reference/serial/redirect) * [redirect to usb](/reference/serial/redirect-to-usb) + * [set baud rate](/reference/serial/set-baud-rate) + * [set write line padding](/reference/serial/set-write-line-padding) * [write buffer](/reference/serial/write-buffer) * [read buffer](/reference/serial/read-buffer) * [Control](/reference/control) @@ -326,14 +327,6 @@ * [stop advertising](/reference/bluetooth/stop-advertising) * [advertise uid](/reference/bluetooth/advertise-uid) * [advertise-uid-buffer](/reference/bluetooth/advertise-uid-buffer) - * [Devices](/reference/devices) - * [tell camera to](/reference/devices/tell-camera-to) - * [tell remote control to](/reference/devices/tell-remote-control-to) - * [raise alert to](/reference/devices/raise-alert-to) - * [on notified](/reference/devices/on-notified) - * [on gamepad button](/reference/devices/on-gamepad-button) - * [signal strength](/reference/devices/signal-strength) - * [on signal strength changed](/reference/devices/on-signal-strength-changed) ## #packages @@ -353,6 +346,21 @@ ## #other +* [Blocks Gallery](/block-gallery) + +## Miscellaneous #misc + +* Miscellaneous + * [About](/about) + * [Support](/support) + * [Translate](/translate) + * [Sharing projects](/share) + * [Offline support](/offline) + * [Save](/save) + * [Sign In](/identity/sign-in) + * [Cloud Sync](/identity/cloud-sync) + * [Home Page Content](/homepage-content) + * [Hardware](/device) * [Data Analysis](/device/data-analysis) * [Plotting with LEDs](/device/data-analysis/led-plotting) @@ -363,6 +371,7 @@ * [Remote Data](/device/data-analysis/remote) * [Error codes](/device/error-codes) * [Foil circuits](/device/foil-circuits) + * [Incompatible Hardware](/device/incompatible) * [MES events](/device/mes-events) * [Pins](/device/pins) * [Reactive](/device/reactive) @@ -374,4 +383,3 @@ * [WebUSB](/device/usb/webusb) * [WebUSB Troubleshoot](/device/usb/webusb/troubleshoot) * [Flashing via HID (CMSIS-DAP)](/hidflash) -* [Blocks Gallery](/block-gallery) diff --git a/docs/about.md b/docs/about.md index 2b0ddcc5599..6d38ceb2505 100644 --- a/docs/about.md +++ b/docs/about.md @@ -17,7 +17,7 @@ The BBC micro:bit is packaged with sensors, radio, microphone, speaker and other ## ~ hint -**Looking to buy a micro:bit?** See the [list of resellers](https://microbit.org/resellers). +**Looking to buy a micro:bit?** See the [list of official products](https://microbit.org/buy/). ## ~ diff --git a/docs/ai-faq.md b/docs/ai-faq.md new file mode 100644 index 00000000000..f0164a05150 --- /dev/null +++ b/docs/ai-faq.md @@ -0,0 +1,30 @@ +# MakeCode AI Features + +## Responsible AI FAQ + +MakeCode is introducing AI-powered features to enhance the learning and teaching experiences on the platform. These tools are designed to provide educational support while maintaining responsible AI practices. + +### AI Features Overview + +MakeCode now includes several AI-powered tools to support both students and educators: + +- **Error Helper**: Helps students understand and resolve coding errors with AI-generated explanations +- **Code Evaluation Tool**: Assists teachers in evaluating and providing feedback on student projects + +### Feature-Specific FAQs + +For detailed information about each AI feature, including their capabilities, limitations, and responsible use guidelines, please visit the specific FAQ pages: + +#### [Error Helper AI FAQ](/errorhelper/ai-faq) +Learn about the AI-powered Error Helper that walks students through exceptions in their projects. + +#### [Code Evaluation Tool AI FAQ](/teachertool/ai-faq) +Learn about the AI tool designed to help teachers evaluate and provide feedback on student programs. + +### General AI Principles + +All MakeCode AI features are designed with the following principles: + +- **Educational Focus**: Tools are specifically designed for learning and teaching scenarios +- **Transparency**: Clear information about what each tool does and its limitations +- **Safety**: Built-in safeguards and content filtering appropriate for educational environments diff --git a/docs/alpha-ref.json b/docs/alpha-ref.json new file mode 100644 index 00000000000..b2abd85dcad --- /dev/null +++ b/docs/alpha-ref.json @@ -0,0 +1,3 @@ +{ + "appref": "v" +} \ No newline at end of file diff --git a/docs/beta-ref.json b/docs/beta-ref.json index 58f715cc151..cd0fd7d91b7 100644 --- a/docs/beta-ref.json +++ b/docs/beta-ref.json @@ -1,3 +1,3 @@ { - "appref": "v" + "appref": "v9.0" } diff --git a/docs/block-gallery.md b/docs/block-gallery.md index c1e7ffe4bea..d0fe8fff6ba 100644 --- a/docs/block-gallery.md +++ b/docs/block-gallery.md @@ -1,6 +1,54 @@ # Blocks Gallery -Listing of the blocks and their images for @boardname@. +Gallery of the blocks and their images for @boardname@. + +## Categories + +```codecard +[ +{ + "name": "Basic", + "description": "Basic display and control blocks.", + "url": "/block-gallery#basic" +},{ + "name": "Input", + "description": "Events and data from buttons and sensors.", + "url": "/block-gallery#input" +},{ + "name": "Music", + "description": "Generation of tones and melodies.", + "url": "/block-gallery#music" +},{ + "name": "Led", + "description": "Display information and images on the LED screen.", + "url": "/block-gallery#led" +},{ + "name": "Radio", + "description": "Transmit and receive data with the radio.", + "url": "/block-gallery#radio" +},{ + "name": "Game", + "description": "Control sprites and keep score in games.", + "url": "/block-gallery#game" +},{ + "name": "Images", + "description": "Create pixel images to display on the LED screen.", + "url": "/block-gallery#images" +},{ + "name": "Pins", + "description": "Read from and write data to the pins on the board.", + "url": "/block-gallery#pins" +},{ + "name": "Serial", + "description": "Use the serial connection to read and write data.", + "url": "/block-gallery#serial" +},{ + "name": "Control", + "description": "Use timers and custom events in programs.", + "url": "/block-gallery#control" +} +] +``` ## Basic diff --git a/docs/blocks/comments.md b/docs/blocks/comments.md new file mode 100644 index 00000000000..cb0e62f3e8d --- /dev/null +++ b/docs/blocks/comments.md @@ -0,0 +1,131 @@ +# Comments + +For simple programs, is easy to understand the steps and the flow the program might take when it runs. If you have just a few steps in your program that run one after the other in a sequence, it's fairly easy to understand what's happening at each place in your program. + +The following program does 4 simple things: + +1. Show a "Hello" message +2. Display a smiley face as part of a greeting +2. Pause for a second so you can see the smiley +3. Clear the screen + +```blocks +basic.showString("Hello!") +basic.showIcon(IconNames.Happy) +basic.pause(1000) +basic.clearScreen() +``` + +It's quite obvious what this program is doing. Each block does one thing, the next block runs after the previous one in a sequence, then the program ends. Let's say you want the user to show that they saw the greeting. You could add button press event that shows a check mark icon. + +```blocks +basic.showString("Hello!") +basic.showIcon(IconNames.Happy) +basic.pause(1000) +basic.clearScreen() +input.onButtonPressed(Button.A, function () { + basic.showIcon(IconNames.Yes) +}) +``` + +You know, of course, that the button press event means that the user has acknowleged your greeting. If you shared your program with someone else though, they might not understand why you wanted to add the button press to the program. + +## Block comments + +To let others know what certain parts of your program are supposed to do, you can add **comments**. Comments are a text description that says what that part of your program is doing. To put a comment on a block, open the block menu and select **Add comment**. + +![Block menu](/static/blocks/block-menu.jpg) + +A place for your comment will appear and you can type in your description of what that block is for and what it's supposed to do. + +![Insert the comment](/static/blocks/insert-comment.jpg) + +Once a comment is added to a block, a comment icon will show in the upper-left corner of the block. Here's our program with the comment on the ``||input:on button A press||``. + +```blocks +// Signal that the greeting was seen +input.onButtonPressed(Button.A, function () { + basic.showIcon(IconNames.Yes) +}) +basic.showString("Hello!") +basic.showIcon(IconNames.Happy) +basic.pause(1000) +basic.clearScreen() +``` + +In the Blocks editor, the comment is displayed when you click on the comment icon. If you view the JavaScript or Python code, you'll see a comment line directly above the button press code. + +```typescript +// Signal that the greeting was seen +input.onButtonPressed(Button.A, function () { + basic.showIcon(IconNames.Yes) +}) +basic.showString("Hello!") +basic.showIcon(IconNames.Happy) +basic.pause(1000) +basic.clearScreen() +``` + +When a program contains conditionals, loops, or functions, adding comments becomes important to help understand what's happening at those places in your program. The program can take a different path based on a condition, result from a function, or some other action. + +### ~ hint + +#### Workspace comments + +You can add comments an notes about your project with **Workspace Comments**. Just right-click on the Workspace background and choose **Add Comment** to insert your comments for the project. + +```block +/** + * This is a workspace comment. + * + * Use this space to make comments + * + * and notes about your project. + */ +// Display a message +function showMessage () { + basic.showString("Workspaces have comments!") +} +``` + +### ~ + +The following example has a conditional inside a loop to choose one of three different mood values. For each mood, a function will display an icon for it. Each block has a comment that describes what it will do. Take a look at the JavaScript or Python code to see the comments on the code text also. + +```blocks +/** + * The mood icon project. + * + * TODO: add more moods + * + * 1. Sad + * + * 2. Confused + */ +// Display an emotion of love +function heart () { + basic.showIcon(IconNames.Heart) +} +// Show a surprised expression +function surprised () { + basic.showIcon(IconNames.Surprised) +} +// Display a smiley mood +function smiley () { + basic.showIcon(IconNames.Happy) +} +// My program that shows three mood icons +// Run the loop to show 3 icons +for (let index = 0; index <= 2; index++) { + // Select an icon based on the index + if (index == 0) { + smiley() + } else if (index == 1) { + heart() + } else { + surprised() + } + // Pause for a second between moods + basic.pause(1000) +} +``` \ No newline at end of file diff --git a/docs/blocks/loops.md b/docs/blocks/loops.md index 03d67b62caa..b6711aabeaf 100644 --- a/docs/blocks/loops.md +++ b/docs/blocks/loops.md @@ -1,3 +1,15 @@ # @extends ## #specific + +```cards +loops.everyInterval(500, function () {}) +``` + +## #seealso + +[for](/blocks/loops/for), +[while](/blocks/loops/while), +[repeat](/blocks/loops/repeat), +[for of](/blocks/loops/for-of), +[every](/reference/loops/every-interval) diff --git a/docs/blocks/pause-until.md b/docs/blocks/pause-until.md new file mode 100644 index 00000000000..2d3cdbf84b9 --- /dev/null +++ b/docs/blocks/pause-until.md @@ -0,0 +1,40 @@ +# pause Until + +Pause the current part of the program until a condition becomes true. + +```sig +pauseUntil(() => true) +``` + +Sometimes you need to wait in one part of a program for something to happen somewhere else in the program. This is done by pausing until some condition elsewhere becomes ``true``. Such a condition could be a value you set in an event block or a function that returns a [boolean](/types/boolean). + +## Parameters + +* **condition**: a [boolean](/types/boolean) condition that restarts the program when it becomes ``true``. +* **timeOut**: an optional paramenter which is a [number](/types/number) of milliseconds to wait for the **condition** to become ``true``. The pause ends when the timeout has elapsed even if **condition** is still ``false``. + +### ~hint + +#### Other code will run too + +The code you have in **events** blocks will continue to execute while the current part of your program is paused. + +### ~ + +## Example + +Wait on a five second timer function. + +```blocks +function waitFiveSeconds() : boolean { + for (let i = 0; i < 5000; i++) { + control.waitMicros(1000) + } + return true +} +pauseUntil(() => waitFiveSeconds()) +``` + +## See also + +[pause](/reference/basic/pause) \ No newline at end of file diff --git a/docs/blocks/variables/assign.md b/docs/blocks/variables/assign.md index 0d3a503ae69..43742765455 100644 --- a/docs/blocks/variables/assign.md +++ b/docs/blocks/variables/assign.md @@ -1,11 +1,34 @@ # Assignment Operator -Use an equals sign to make a [variable](/blocks/variables/var) store the [number](/types/number) -or [string](/types/string) you say. +Use an equals sign to make a [variable](/blocks/variables/var) store a [number](/types/number), [string](/types/string), or other [type](/types) of value. -When you use the equals sign to store something in a variable, the equals sign is called +When you use the equals sign (**=**) to store something in a variable, the equals sign is called an *assignment operator*, and what you store is called a *value*. +When you work in JavaScript or Python the equals sign is used: + +```typescript-ignore +item = 3 +``` + +## The 'set' block + +In blocks, a variable assignment happens with the ``||variables:set||`` block when you set a value to a [variable](/blocks/variables/var). + +```block +let item = 3 +``` + +### ~ hint + +#### Setting a variable using the Toolbox + +In the ``||variables:Variables||`` category of the **Toolbox** you can create or choose a variable to assign: + +![Setting a variable to a value](/static/blocks/variables/assign.gif) + +### ~ + ## Storing numbers in variables This program makes the variable `item` equal `5` and then shows it on the [LED screen](/device/screen). diff --git a/docs/blocks/variables/var.md b/docs/blocks/variables/var.md index d149e444211..61ef79cde42 100644 --- a/docs/blocks/variables/var.md +++ b/docs/blocks/variables/var.md @@ -1,38 +1,60 @@ -# Local Variables +# Declaring variables -How to define and use local variables. +How to declare and use variables. ## @parent language -A variable is a place where you can store and retrieve data. Variables have a name, a [type](/types), and value: +A variable is a place where you can store and retrieve data. Variables have a name, a [type](/types), and a value: * *name* is how you'll refer to the variable -* *type* refers to the kind of data a variable can store -* *value* refers to what's stored in the variable +* *type* is the kind of data a variable can store +* *value* is what's stored in the variable -## Var statement +## The variable (var) statement -Use the Block Editor variable statement to create a variable -and the [assignment operator](/blocks/variables/assign) -to store something in the variable. +A variable is created using a `variable` statement. The variable will "declare" itself in this statement. In MakeCode JavaScript a variable is declared along with its first [assignment](/blocks/variables/assign): -For example, this code stores the number `2` in the `x` variable: +```typescript +let x = 2 +``` + +With Python a variable is declared when it's first used. In this case the variable statement is just the assignment of the variable: + +```python +x = 2 +``` + +If a variable is declared in blocks you will see the ``||variables:set||`` block with the first use of the variable. This code stores the number `2` in the `x` variable: ```blocks -let x = 2; +let x = 2 ``` -Here's how to define a variable in the Block Editor: -1. Click `variables`. +The new variable is created inside the ``||variables:Variables||`` category of the **Toolbox**. + +### ~ hint -2. Change the default variable name if you like. +#### Creating a variable from the Toolbox -3. Drag a block type on the right-side of the [assignment operator](/blocks/variables/assign) and click the down arrow to change the variable name. +In the ``||variables:Variables||`` category of the **Toolbox** you can create new variable: + +![Create a new variable](/static/blocks/variables/create.gif) + +Here's how to create a variable using the Toolbox: + +1. Click ``||variables:Variables||`` in the Toolbox. +2. Click on **Make a Variable...**. +3. Choose a name for your variable, type it in, and click **Ok**. +4. Drag the new variable, ``||variables:set||`` or ``||variables:change||`` block into your code. + +### ~ + +### Quick example A variable is created for the number returned by the [brightness](/reference/led/brightness) function. ```blocks -let b = led.brightness(); +let b = led.brightness() ``` ## Using variables @@ -40,16 +62,16 @@ let b = led.brightness(); Once you've defined a variable, just use the variable's name whenever you need what's stored in the variable. For example, the following code shows the value stored in `counter` on the LED screen: ```blocks -let counter = 1; -basic.showNumber(counter); +let counter = 1 +basic.showNumber(counter) ``` To change the contents of a variable use the assignment operator. The following code sets `counter` to 1 and then increments `counter` by 10: ```blocks -let counter = 1; -counter = counter + 10; -basic.showNumber(counter); +let counter = 1 +counter = counter + 10 +basic.showNumber(counter) ``` ## Why use variables? @@ -58,11 +80,11 @@ If you want to remember and modify data, you'll need a variable. A counter is a great example: ```blocks -let counter = 0; -input.onButtonPressed(Button.A, () => { - counter = counter + 1; - basic.showNumber(counter); -}); +let counter = 0 +input.onButtonPressed(Button.A, function () { + counter = counter + 1 + basic.showNumber(counter) +}) ``` ## Local variables @@ -72,16 +94,38 @@ Local variables exist only within the function or block of code where they're de ```blocks // x does NOT exist here. if (led.brightness() > 128) { - // x exists here - let x = 0; + // x exists here + let x = 0 } ``` -### Notes +## Notes about variables + +### Variable names -* You can use the default variable names if you'd like, however, it's best to use descriptive variable names. To change a variable name in the editor, select the down arrow next to the variable and then click "new variable". +Some blocks come from the Toolbox with default variable names, such as `list` from ``||arrays:Arrays||``. +You can use the default variable names if you like, however, it's best to use descriptive variable names. To change a variable name in the editor, select the down arrow next to the variable and then click **"Rename variable..."** to change it. + +### Hidden declaration block + +If a variable is used more than once, and its declaration block sets the variable to `0`, that first declaration block is hidden. For example: + +```blocks +let x = 0 +if (x == 0) { + x = 9 +} +``` + +You don't see any first block setting the variable `x` to `0` but that happens in its hidden declaration statement. You'll see the declaration statement, `let x = 0`, if you switch from Blocks to JavaScript in the Editor: + +```typescript-ignore +let x = 0 +if (x == 0) { + x = 9 +} +``` ## See also [types](/types), [assignment operator](/blocks/variables/assign) - diff --git a/docs/code-eval-tool.md b/docs/code-eval-tool.md new file mode 100644 index 00000000000..189df727eca --- /dev/null +++ b/docs/code-eval-tool.md @@ -0,0 +1,192 @@ +# Code Evaluation Tool + +## Overview + +The [Code Evaluation Tool]( https://microbit.makecode.com/--eval) is a mechanism for constructing a checklist of requirements for an assignment and running that list automatically against projects in quick succession. This allows teachers to build a checklist, then easily evaluate any number of projects based on that checklist. Projects are evaluated one at a time, but with auto-run enabled, you can update the loaded project by providing a new share link, at which point the rules will automatically be re-run on the new project. + +## Code Evaluation Tool Features + +### Creating, Editing, and Running a Checklist + +#### 1. Creating a new checklist + +Create a new checklist using the **New Checklist** card. If there is already an "in progress" checklist, a warning will appear asking if it is okay to overwrite it. + +![New Checklist](/static/teachertool/new-rubric.png) + +![New Checklist from menu](/static/teachertool/new-rubric-from-menu.png) + +#### 2. Naming a checklist + +The checklist is given a name. + +![Checklist name](/static/teachertool/checklist-name.png) + +#### 3. Add Criteria + +One or more **_criteria_** are added from the catalog using the **Add Criteria** button. + +![Add Criteria](/static/teachertool/add-criteria.png) + +Some criteria (like `[block] used [count] times`) can be added multiple times, others (like `Read a GPIO pin` can only be added once). + +![Criteria items](/static/teachertool/criteria-items.png) + +#### 4. Fill in Parameters + +Parameters for the criteria item are filled in for a criteria item. + +### ~ tip + +#### Parameter types + +From a technical perspective, criteria parameters have these types: + +- **Numeric** parameters have a small input and only allow number inputs. +- **String** parameters can have medium and long sized inputs. +- **Block** parameters should open a block-picker modal. +- **Empty** parameters appear in an error state until they have values. + +### ~ + +Here a block is selected and used 3 times: + +![Criteria parameters 1](/static/teachertool/parameters-1.png) + +Parameter options are displayed and then selected. + +![Criteria parameters 2](/static/teachertool/parameters-2.png) + +![Criteria parameters 3](/static/teachertool/parameters-3.png) + +#### 5. Ask AI + +You can also have an **Ask AI** question as a criteria item in the checklist. You are limited to up to 5 Ask AI questions per checklist. + +![Ask AI criteria](/static/teachertool/ask-ai-criteria.png) + +### ~hint + +#### AI usage + +The use of AI in criteria items is further explained in the [AI FAQ](/teachertool/ai-faq). + +### ~ + +#### 6. Remove Criteria + +A criteria item is removed using the **trash** button. + +![Remove Criteria](/static/teachertool/remove-criteria.png) + +#### 7. Load a project + +Load a project into the project view by pasting in a share link or share ID. + +![A loaded project](/static/teachertool/loaded-project.png) + +The project will load in read-only mode with the project title appearing at the top of the project view. + +![Project validation](/static/teachertool/validate-me.png) + +#### 8. Run the checklist + +With a project loaded, the checklist can run. The results are shown after clicking the **Run** button. + +![Run checklist](/static/teachertool/run-checklist-button.png) + +The results view lists each criteria with its outcome. + +![Checklist execution](/static/teachertool/checklist-execution.png) + +**Note**: the **Run** Button is disabled without loaded project. + +### Editing Results + +#### 1. Add feedback and notes + +Feedback and notes are added using the **Add Notes** button. The feedback box should resize to fit its content as notes are added. The original feedback remains even if you re-run the rules using the **Run** button. + +![Editing results](/static/teachertool/editing-results-1.png) + +![Editing results](/static/teachertool/editing-results-2.png) + +![Editing results](/static/teachertool/editing-results-3.png) + +#### 2. Edit outcomes + +An outcome is edited using the provided dropdown. + +![Edit outcome](/static/teachertool/edit-outcome-1.png) + +The new selected outcome. + +![Edit outcome](/static/teachertool/edit-outcome-2.png) + +### Result Clearing and Auto-Run + +#### 1. Toggling Auto-run + +Auto-run is toggled either **on** or **off** using the button in the menu. + +![Auto-run button](/static/teachertool/autorun-button.png) + +#### 2. Auto-run disabled + +If auto-run is **disabled**, a result's outcome (i.e. "Looks good", "Needs work", etc...) is set to "Not started" automatically if any of any of the following conditions are met: + +- It is newly added (defaults to the "Not Started" state). +- A parameter in a rule is changed (only the affected rule enters the "Not Started" state). +- The loaded project changes (all rules are be set to "Not started"). + +#### 3. Auto-run enabled + +If auto-run is **enabled**, any rules that enter the "Not started" state due to the conditions listed above are immediately and automatically re-run with their results updated. + +### Loading/Importing/Exporting Checklists + +#### 1. Pre-built checklists + +There are pre-built checklists are available on the welcome page. If a selected checklist is already in-progress, an overwrite confirmation prompt is given. + +![Pre-built checklists](/static/teachertool/prebuilt-rubrics.png) + +#### 2. Export a checklist + +A checklist is exported using the vertical "..." menu near the "auto-run" button. Only the checklist is exported, not the results. For a copy of the results, use the [print](#other) function in the Results view. + +![Export checklist](/static/teachertool/export-checklist.png) + +This will download a json file for the checklist. + +![Checklist download](/static/teachertool/checklist-download.png) + +#### 3. Import a checklist + +User can import a checklist from a file using the same "..." menu, or from the card on the welcome page. + +![Import checklist card](/static/teachertool/import-checklist-card.png) + +![Import checklist menu](/static/teachertool/import-checklist-menu.png) + +Checklist file is selected using "Browse" or dropped directly into the popup. An overwrite confirmation prompted if there is currently an in-progress checklist. + +![Import checklist drag in](/static/teachertool/import-checklist-dragdrop-1.png) + +![Import checklist drop off](/static/teachertool/import-checklist-dragdrop-2.png) + +### Other + +If the page is refreshed (or if the browser closes/re-opens), the current checklist preserved. + +Use the print button to create a version of the results with the outcomes and feedback visible (the other UI elements are hidden). + +![Print button](/static/teachertool/print-button.png) + +The checklist-view/project-view splitter can be resized. It can also be reset to 50/50 split with double-click. + +![View splitter button](/static/teachertool/view-splitter.png) + +Slide the splitter to widen the view of the criteria and results. + +![Split view resize](/static/teachertool/split-resize.png) diff --git a/docs/courses.md b/docs/courses.md index fa0c63bac68..d3a015e15e6 100644 --- a/docs/courses.md +++ b/docs/courses.md @@ -30,6 +30,11 @@ Courses contributed by educators to teach computing, science, and technology in "description": "Science experiment lessons with measurements and data analysis activities", "url":"/courses/ucp-science", "imageUrl": "/static/courses/ucp-science.jpg" +}, { + "name": "Cyber Arcade: Programming and Making with micro:bit", + "description": "A fun and creative introduction to computer science and hands-on making for makers in elementary (ages 9–12) and middle (ages 12–14) grade levels with little to no experience in programming and 3D design.", + "url":"https://makered.org/resources/cyber-arcade-programming-and-making-with-microbit/", + "imageUrl": "/static/courses/maker-ed-cyber-arcade.png" }, { "name": "Learn All About micro:bit", "description": "Projects and integration notes for a student-led workshop from the Beacon Hill School", @@ -43,6 +48,29 @@ Courses contributed by educators to teach computing, science, and technology in }] ``` +## Mr. Morrison Lessons + +Lessons aimed at P4-7 (Yr 3-6, aged 7-12) but could be adapted for use with older or younger learners. Regardless of age, if your learners have not used micro:bits before the best place to start is the 'Starter Lessons' followed by the 'Beyond Basics' Lessons. + +```codecard +[{ + "name": "micro:bit Starter Lessons", + "description": "Learn to create code, make programs to read inputs and write to outputs.", + "url": "https://mrmorrison.co.uk/microbit/starter/", + "imageUrl": "/static/courses/mr-morrison/starter-lessons.png" +}, { + "name": "micro:bit Beyond Basics", + "description": "Take a step past the basics and learn to use logic with inputs and outputs.", + "url": "https://mrmorrison.co.uk/microbit/beyondbasics/", + "imageUrl": "/static/courses/mr-morrison/beyond-basics.png" +}, { + "name": "micro:bit Data and Sustainability", + "description": "Learn to record and analyse data using the micro:bit, then learn to design and build a smart sustainable home.", + "url": "https://mrmorrison.co.uk/microbit/datasustainability/", + "imageUrl": "/static/courses/mr-morrison/data-sustainability.png" +}] +``` + ## Computers and programming Tutorials, lessons, and mini-courses about programming and computing. @@ -61,18 +89,24 @@ Tutorials, lessons, and mini-courses about programming and computing. }, { "name": "Networking with the micro:bit", "description": "A series of activities to teach the basics of computer networks.", - "url": "https://microbit.org/projects/make-it-code-it/", + "url": "https://www.digitaltechnologieshub.edu.au/search/networking-with-the-micro-bit/", "imageUrl": "/static/courses/networking-book.png" }, { "name": "SparkFun Videos", "description": "YouTube video tutorials produced by the SparkFun team!", - "url": "https://youtu.be/kaNtg1HGXbY?list=PLBcrWxTa5CS0mWJrytvii8aG5KUqMXvSk", + "youTubeId": "kaNtg1HGXbY", + "youTubePlaylistId": "PLBcrWxTa5CS0mWJrytvii8aG5KUqMXvSk", "imageUrl": "https://i.ytimg.com/vi/kaNtg1HGXbY/hqdefault.jpg" }, { "name": "Logic Lab", "description": "Learn the basics of logic and conditional expressions.", "url":"/courses/logic-lab", "imageUrl":"/static/courses/logic-lab.png" +}, { + "name": "CodeJoy Remote Robotics", + "description": "Interactive remote robotics and coding classes for students and educators", + "url": "https://www.codejoy.org", + "imageUrl": "/static/courses/codejoy.png" }] ``` @@ -101,6 +135,11 @@ Fun project courses - make and experiment while learning about science and progr "description": "A hands-on course about the micro:bit and what you can do with it.", "url":"https://sites.google.com/view/microbitofthings", "imageUrl": "/static/courses/microbit-of-things.jpg" +}, { + "name": "ARM University - micro:course", + "description": "Introduce learners to the world of making and programming through a series of real-world challenges that feature the micro:bit.", + "url": "https://github.com/arm-university/micro-course", + "imageUrl": "/static/courses/armu-micro-course.png" }, { "name": "A-Z Robotics", "description": "Absolute beginner's guide to learning coding, electronics and robotics on the micro:bit", @@ -111,4 +150,4 @@ Fun project courses - make and experiment while learning about science and progr ## See Also -[Intro to CS](/courses/csintro) \ No newline at end of file +[Intro to CS](/courses/csintro) diff --git a/docs/courses/blocks-to-javascript/complex-conditionals.md b/docs/courses/blocks-to-javascript/complex-conditionals.md index c83fef5477d..ad79c6f6098 100644 --- a/docs/courses/blocks-to-javascript/complex-conditionals.md +++ b/docs/courses/blocks-to-javascript/complex-conditionals.md @@ -71,9 +71,9 @@ basic.forever(function () { The two ``||input:button is pressed||`` blocks are connected with an ``||logic:or||`` in the ``||logic:if then||`` conditional to make the check to see if one of the two buttons is currently pressed. -Looking at the condition inside the ``if`` statement in JavaScript editor, we see that the two conditions are connected with the OR (``||``) operator which makes the condition `true` if either button is pressed. So, the conditions work in _parallel_. +Looking at the condition inside the ``if`` statement in the JavaScript editor, we see that the two conditions are connected with the OR (``||``) operator which makes the condition `true` if either button is pressed. So, the conditions work in _parallel_. -```typescript +```typescript-ignore if (input.buttonIsPressed(Button.A) || input.buttonIsPressed(Button.B)) { basic.showIcon(IconNames.Square) } @@ -115,7 +115,7 @@ let cold = input.temperature() < 10 In the JavaScript editor, this block appears as this simple variable assignment: -```typescript +```typescript-ignore let cold = input.temperature() < 10 ``` diff --git a/docs/courses/blocks-to-javascript/conditional-loops.md b/docs/courses/blocks-to-javascript/conditional-loops.md index 9eb32f7226e..3dbe2d2e87b 100644 --- a/docs/courses/blocks-to-javascript/conditional-loops.md +++ b/docs/courses/blocks-to-javascript/conditional-loops.md @@ -79,7 +79,7 @@ let count = 0 } while (count < 5) ``` -You'll have a squiggle at the end of the line with ``} while (count < 5)`` which means we have invalid code. Don't worry, we'll fix that right now! At the beginning of the loop, insert the word ``do`` just before left brace `{`. The error should go away and the simulator will count from `0` to `4` just like before when the word ``while`` was at the beginning of the loop. +You'll have a squiggle at the end of the line with ``} while (count < 5)`` which means we have invalid code. Don't worry, we'll fix that right now! At the beginning of the loop, insert the word ``do`` just before left brace `{`. The error should go away and the simulator will count from `0` to `4` just like before when the word ``while`` was at the beginning of the loop. ```typescript let count = 0 @@ -115,7 +115,7 @@ for (let index = 4; index >= 0; index--) { You'll now see in the simulator that the value displayed on the screen counts down from `4` to `0`. This form of the **for** loop is too complicatied for blocks so when you switch back to the Blocks editor the entire loop is shown in a grey block. -```block +```block-ignore for (let index = 4; index >= 0; index--) { basic.showNumber(index) basic.pause(500) @@ -196,4 +196,4 @@ for (let index = 0; index < 5; index++) { } ``` -Ah! Our original ``||loops:repeat||`` loop changed to a ``||loops:for||`` loop! Why? Well, this is because the code inside the loop used the index from the loop statement. This makes the loop more complex and it's no longer just simply repeating what's inside of it. +Ah! Our original ``||loops:repeat||`` loop changed to a ``||loops:for||`` loop! Why? Well, this is because the code inside the loop used the index from the loop statement. This makes the loop more complex and it's no longer just simply repeating what's inside of it. diff --git a/docs/courses/blocks-to-javascript/hello-javascript.md b/docs/courses/blocks-to-javascript/hello-javascript.md index 281404945bc..5f3dfbcb6b1 100644 --- a/docs/courses/blocks-to-javascript/hello-javascript.md +++ b/docs/courses/blocks-to-javascript/hello-javascript.md @@ -165,6 +165,6 @@ basic.forever(function () { ## ~ avatar -Well done! You've just coded using a "text programming" language! Starting from blocks, you learned to convert them to JavaScript and then modify the code in the blocks as text. Wow, you're a pro now! Go back to [Blocks To JavaScript](/projects/blocks-to-javascript) to continue with another challenge. +Well done! You've just coded using a "text programming" language! Starting from blocks, you learned to convert them to JavaScript and then modify the code in the blocks as text. Wow, you're a pro now! Go back to [Blocks To JavaScript](/courses/blocks-to-javascript) to continue with another challenge. ## ~ diff --git a/docs/courses/blocks-to-javascript/starter-blocks.md b/docs/courses/blocks-to-javascript/starter-blocks.md index f8ce7f030b4..318372d61a0 100644 --- a/docs/courses/blocks-to-javascript/starter-blocks.md +++ b/docs/courses/blocks-to-javascript/starter-blocks.md @@ -81,13 +81,13 @@ input.onButtonPressed(Button.A, function () { ## Grey blocks are a good sign! If you start to write more complex JavaScript (it's great that you are!), MakeCode might not be able to convert it back to blocks. In that case, you will see _grey blocks_ in your block code. They represent chunks of JavaScript that are too complicated for blocks. - + * Go back to **JavaScript** and add a second frame to create animation. This is something you can do in JavaScript but not in blocks. ```typescript input.onButtonPressed(Button.A, function () { basic.showLeds(` - . . . . . . . . . . + . . . . . . . . . . . . # . . . # # # . . # # # . . # # # . . . # . . . # # # . @@ -98,10 +98,10 @@ input.onButtonPressed(Button.A, function () { * Go to the **Blocks** editor and you will see a big **grey** block in the button handler. This is because you are creating code too complex for the blocks. Take it as a compliment! -```blocks +```blocks-ignore input.onButtonPressed(Button.A, function () { basic.showLeds(` - . . . . . . . . . . + . . . . . . . . . . . . # . . . # # # . . # # # . . # # # . . . # . . . # # # . diff --git a/docs/courses/csintro-educator.md b/docs/courses/csintro-educator.md index 52020f632c2..f0c8a3e1c8d 100644 --- a/docs/courses/csintro-educator.md +++ b/docs/courses/csintro-educator.md @@ -22,14 +22,20 @@ Any of the individual course material items are also available as a separate dow ### [Course overview](https://onedrive.live.com/?authkey=%21ALunv1kXkaA0RLg&id=416406873CB120AB%21520&cid=416406873CB120AB) -* [Course overview guide](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!525&parId=416406873CB120AB!520&authkey=!ALunv1kXkaA0RLg&app=Word) -* [Curriculum overview](https://onedrive.live.com/?authkey=%21ALunv1kXkaA0RLg&cid=416406873CB120AB&id=416406873CB120AB%21524&parId=416406873CB120AB%21520&o=OneUp) -* [Educator preparation video](https://onedrive.live.com/?authkey=%21ALunv1kXkaA0RLg&cid=416406873CB120AB&id=416406873CB120AB%21526&parId=416406873CB120AB%21520&o=OneUp) +* [Course overview booklet](https://onedrive.live.com/view.aspx?resid=416406873CB120AB!3747&cid=416406873cb120ab&authkey=!ALunv1kXkaA0RLg&CT=1686690655374&OR=ItemsView) +* [Course overview booklet (PDF)](https://onedrive.live.com/?authkey=%21ALunv1kXkaA0RLg&cid=416406873CB120AB&id=416406873CB120AB%213746&parId=416406873CB120AB%21520&o=OneUp) +* [Curriculum overview guide](https://onedrive.live.com/view.aspx?resid=416406873CB120AB!3749&cid=416406873cb120ab&authkey=!ALunv1kXkaA0RLg&CT=1686690921127&OR=ItemsView) +* [Curriculum overview guide (PDF)](https://onedrive.live.com/?authkey=%21ALunv1kXkaA0RLg&cid=416406873CB120AB&id=416406873CB120AB%213748&parId=416406873CB120AB%21520&o=OneUp) +* [Educator preparation video](https://youtu.be/q6x_tj5DazY) +* [Educator preparation video (PowerPoint)](https://onedrive.live.com/view.aspx?resid=416406873CB120AB!3750&cid=416406873cb120ab&authkey=!ALunv1kXkaA0RLg&CT=1686690587199&OR=ItemsView) ### [Standards and assessments](https://onedrive.live.com/?authkey=%21ALunv1kXkaA0RLg&id=416406873CB120AB%21521&cid=416406873CB120AB) * [Assessment guide](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!528&parId=416406873CB120AB!521&authkey=!ALunv1kXkaA0RLg&app=Word) +* [Assesment guide (PDF)](https://onedrive.live.com/?authkey=%21ALunv1kXkaA0RLg&cid=416406873CB120AB&id=416406873CB120AB%213751&parId=416406873CB120AB%21521&o=OneUp) * [Standards alignment guide](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!527&parId=416406873CB120AB!521&authkey=!ALunv1kXkaA0RLg&app=Word) +* [Standards alignment guide (PDF)](https://onedrive.live.com/?authkey=%21ALunv1kXkaA0RLg&cid=416406873CB120AB&id=416406873CB120AB%213752&parId=416406873CB120AB%21521&o=OneUp) + ### [Unit materials](https://onedrive.live.com/?authkey=%21ALunv1kXkaA0RLg&id=416406873CB120AB%21522&cid=416406873CB120AB) @@ -38,85 +44,105 @@ Any of the individual course material items are also available as a separate dow * [Classroom presentation](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!556&parId=416406873CB120AB!542&authkey=!AM4rnlyFUI-sP9Y&app=PowerPoint) * [Educator Guide](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!554&parId=416406873CB120AB!542&authkey=!AM4rnlyFUI-sP9Y&app=Word) * [Student workbook](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!555&parId=416406873CB120AB!542&authkey=!AM4rnlyFUI-sP9Y&app=Word) -* [Quick start video](https://onedrive.live.com/?authkey=%21AM4rnlyFUI%2DsP9Y&cid=416406873CB120AB&id=416406873CB120AB%21557&parId=416406873CB120AB%21542&o=OneUp) +* [Quick start video](https://youtu.be/J9aRFvBB7T4) +* [Quick start video (PowerPoint)](https://onedrive.live.com/view.aspx?resid=416406873CB120AB!3753&cid=416406873cb120ab&authkey=!ALunv1kXkaA0RLg&CT=1686690011517&OR=ItemsView) #### [Unit 2 - Algorithms](https://1drv.ms/f/s!AqsgsTyHBmRBhB9mHogz24TrrXvd) * [Classroom presentation](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!560&parId=416406873CB120AB!543&authkey=!AGYeiDPbhOute90&app=PowerPoint) * [Educator Guide](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!558&parId=416406873CB120AB!543&authkey=!AGYeiDPbhOute90&app=Word) * [Student workbook](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!559&parId=416406873CB120AB!543&authkey=!AGYeiDPbhOute90&app=Word) -* [Quick start video](https://onedrive.live.com/?authkey=%21AGYeiDPbhOute90&cid=416406873CB120AB&id=416406873CB120AB%21561&parId=416406873CB120AB%21543&o=OneUp) +* [Quick start video](https://youtu.be/xfQ8f9rpNXo) +* [Quick start video (PowerPoint)](https://onedrive.live.com/view.aspx?resid=416406873CB120AB!3755&cid=416406873cb120ab&authkey=!ALunv1kXkaA0RLg&CT=1686690051176&OR=ItemsView) #### [Unit 3 - Variables](https://1drv.ms/f/s!AqsgsTyHBmRBhCA3Amk-zPfMl-7q) * [Classroom presentation](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!564&parId=416406873CB120AB!544&authkey=!ADcCaT7M98yX7uo&app=PowerPoint) * [Educator Guide](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!562&parId=416406873CB120AB!544&authkey=!ADcCaT7M98yX7uo&app=Word) * [Student workbook](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!563&parId=416406873CB120AB!544&authkey=!ADcCaT7M98yX7uo&app=Word) -* [Quick start video](https://onedrive.live.com/?authkey=%21ADcCaT7M98yX7uo&cid=416406873CB120AB&id=416406873CB120AB%21565&parId=416406873CB120AB%21544&o=OneUp) +* [Quick start video](https://youtu.be/bndvYqROWBI) +* [Quick start video (PowerPoint)](https://onedrive.live.com/view.aspx?resid=416406873CB120AB!3756&cid=416406873cb120ab&authkey=!ALunv1kXkaA0RLg&CT=1686690101309&OR=ItemsView) #### [Unit 4 - Conditionals](https://1drv.ms/f/s!AqsgsTyHBmRBhCEhD98j9NcTVsYj) * [Classroom presentation](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!568&parId=416406873CB120AB!545&authkey=!ACEP3yP01xNWxiM&app=PowerPoint) * [Educator Guide](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!566&parId=416406873CB120AB!545&authkey=!ACEP3yP01xNWxiM&app=Word) * [Student workbook](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!567&parId=416406873CB120AB!545&authkey=!ACEP3yP01xNWxiM&app=Word) -* [Quick start video](https://onedrive.live.com/?authkey=%21ACEP3yP01xNWxiM&cid=416406873CB120AB&id=416406873CB120AB%21569&parId=416406873CB120AB%21545&o=OneUp) +* [Quick start video](https://youtu.be/pQsk4u5oYGU) +* [Quick start video (PowerPoint)](https://onedrive.live.com/view.aspx?resid=416406873CB120AB!3757&cid=416406873cb120ab&authkey=!ALunv1kXkaA0RLg&CT=1686690162923&OR=ItemsView) #### [Unit 5 - Iteration](https://1drv.ms/f/s!AqsgsTyHBmRBhCKvf1jSYdQITB2h) * [Classroom presentation](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!572&parId=416406873CB120AB!546&authkey=!AK9_WNJh1AhMHaE&app=PowerPoint) * [Educator Guide](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!570&parId=416406873CB120AB!546&authkey=!AK9_WNJh1AhMHaE&app=Word) * [Student workbook](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!571&parId=416406873CB120AB!546&authkey=!AK9_WNJh1AhMHaE&app=Word) -* [Quick start video](https://onedrive.live.com/?authkey=%21AK9%5FWNJh1AhMHaE&cid=416406873CB120AB&id=416406873CB120AB%21573&parId=416406873CB120AB%21546&o=OneUp) +* [Quick start video](https://youtu.be/MsidOfiVvyU) +* [Quick start video (PowerPoint)](https://onedrive.live.com/view.aspx?resid=416406873CB120AB!3758&cid=416406873cb120ab&authkey=!ALunv1kXkaA0RLg&CT=1686690193781&OR=ItemsView) #### [Unit 6 - Mini Project](https://1drv.ms/f/s!AqsgsTyHBmRBhCNesBFAojwe-bor) * [Classroom presentation](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!576&parId=416406873CB120AB!547&authkey=!AF6wEUCiPB75uis&app=PowerPoint) * [Educator Guide](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!574&parId=416406873CB120AB!547&authkey=!AF6wEUCiPB75uis&app=Word) * [Student workbook](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!575&parId=416406873CB120AB!547&authkey=!AF6wEUCiPB75uis&app=Word) -* [Quick start video](https://onedrive.live.com/?authkey=%21AF6wEUCiPB75uis&cid=416406873CB120AB&id=416406873CB120AB%21577&parId=416406873CB120AB%21547&o=OneUp) +* [Quick start video](https://youtu.be/s3bN64D_p3Q) +* [Quick start video (PowerPoint)](https://onedrive.live.com/view.aspx?resid=416406873CB120AB!3759&cid=416406873cb120ab&authkey=!ALunv1kXkaA0RLg&CT=1686690265627&OR=ItemsView) #### [Unit 7 - Coordinates](https://1drv.ms/f/s!AqsgsTyHBmRBhCTASW4OemOKnMEv) * [Classroom presentation](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!580&parId=416406873CB120AB!548&authkey=!AMBJbg56Y4qcwS8&app=PowerPoint) * [Educator Guide](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!578&parId=416406873CB120AB!548&authkey=!AMBJbg56Y4qcwS8&app=Word) * [Student workbook](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!579&parId=416406873CB120AB!548&authkey=!AMBJbg56Y4qcwS8&app=Word) -* [Quick start video](https://onedrive.live.com/?authkey=%21AMBJbg56Y4qcwS8&cid=416406873CB120AB&id=416406873CB120AB%21581&parId=416406873CB120AB%21548&o=OneUp) +* [Quick start video](https://youtu.be/FIofhOP8Ftk) +* [Quick start video (PowerPoint)](https://onedrive.live.com/view.aspx?resid=416406873CB120AB!3760&cid=416406873cb120ab&authkey=!ALunv1kXkaA0RLg&CT=1686690325195&OR=ItemsView) #### [Unit 8 - Booleans](https://1drv.ms/f/s!AqsgsTyHBmRBhCVCoNzaW1aTHQzm) * [Classroom presentation](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!584&parId=416406873CB120AB!549&authkey=!AEKg3NpbVpMdDOY&app=PowerPoint) * [Educator Guide](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!582&parId=416406873CB120AB!549&authkey=!AEKg3NpbVpMdDOY&app=Word) * [Student workbook](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!583&parId=416406873CB120AB!549&authkey=!AEKg3NpbVpMdDOY&app=Word) -* [Quick start video](https://onedrive.live.com/?authkey=%21AEKg3NpbVpMdDOY&cid=416406873CB120AB&id=416406873CB120AB%21585&parId=416406873CB120AB%21549&o=OneUp) +* [Quick start video](https://youtu.be/omi1r8rggpE) +* [Quick start video (PowerPoint)](https://onedrive.live.com/view.aspx?resid=416406873CB120AB!3761&cid=416406873cb120ab&authkey=!ALunv1kXkaA0RLg&CT=1686690357689&OR=ItemsView) #### [Unit 9 - Binary](https://1drv.ms/f/s!AqsgsTyHBmRBhCaJTPDfFM9PABs1) * [Classroom presentation](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!588&parId=416406873CB120AB!550&authkey=!AIlM8N8Uz08AGzU&app=PowerPoint) * [Educator Guide](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!586&parId=416406873CB120AB!550&authkey=!AIlM8N8Uz08AGzU&app=Word) * [Student workbook](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!587&parId=416406873CB120AB!550&authkey=!AIlM8N8Uz08AGzU&app=Word) -* [Quick start video](https://onedrive.live.com/?authkey=%21AIlM8N8Uz08AGzU&cid=416406873CB120AB&id=416406873CB120AB%21589&parId=416406873CB120AB%21550&o=OneUp) +* [Quick start video](https://youtu.be/nVUW_yYoTdU) +* [Quick start video (PowerPoint)](https://onedrive.live.com/view.aspx?resid=416406873CB120AB!3763&cid=416406873cb120ab&authkey=!ALunv1kXkaA0RLg&CT=1686690413779&OR=ItemsView) #### [Unit 10 - Radio](https://1drv.ms/f/s!AqsgsTyHBmRBhCeq5h3BbHNmCpGA) * [Classroom presentation](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!592&parId=416406873CB120AB!551&authkey=!AKrmHcFsc2YKkYA&app=PowerPoint) * [Educator Guide](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!590&parId=416406873CB120AB!551&authkey=!AKrmHcFsc2YKkYA&app=Word) * [Student workbook](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!591&parId=416406873CB120AB!551&authkey=!AKrmHcFsc2YKkYA&app=Word) -* [Quick start video](https://onedrive.live.com/?authkey=%21AKrmHcFsc2YKkYA&cid=416406873CB120AB&id=416406873CB120AB%21593&parId=416406873CB120AB%21551&o=OneUp) +* [Quick start video](https://youtu.be/ugLWoNIoGAo) +* [Quick start video (PowerPoint)](https://onedrive.live.com/view.aspx?resid=416406873CB120AB!3764&cid=416406873cb120ab&authkey=!ALunv1kXkaA0RLg&CT=1686690445091&OR=ItemsView) #### [Unit 11 - Arrays](https://1drv.ms/f/s!AqsgsTyHBmRBhChTGr9RP7MXejC-) * [Classroom presentation](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!596&parId=416406873CB120AB!552&authkey=!AFMav1E_sxd6ML4&app=PowerPoint) * [Educator Guide](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!594&parId=416406873CB120AB!552&authkey=!AFMav1E_sxd6ML4&app=Word) * [Student workbook](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!595&parId=416406873CB120AB!552&authkey=!AFMav1E_sxd6ML4&app=Word) -* [Quick start video](https://onedrive.live.com/?authkey=%21AFMav1E%5Fsxd6ML4&cid=416406873CB120AB&id=416406873CB120AB%21597&parId=416406873CB120AB%21552&o=OneUp) +* [Quick start video](https://youtu.be/B1b4IDrk2mM) +* [Quick start video (PowerPoint)](https://onedrive.live.com/view.aspx?resid=416406873CB120AB!3766&cid=416406873cb120ab&authkey=!ALunv1kXkaA0RLg&CT=1686690475716&OR=ItemsView) + +#### [Unit 12 - Accelerometer](https://1drv.ms/f/c/416406873cb120ab/EqsgsTyHBmQggEEqDAAAAAABv8UVYPZfbSfLgXyKwIYi0Q) + +* [Classroom presentation](https://onedrive.live.com/view.aspx?resid=416406873CB120AB!3772&cid=416406873cb120ab&authkey=!ALunv1kXkaA0RLg&CT=1686689659525&OR=ItemsView) +* [Educator Guide](https://onedrive.live.com/view.aspx?resid=416406873CB120AB!3768&cid=416406873cb120ab&authkey=!ALunv1kXkaA0RLg&CT=1686689325290&OR=ItemsView) +* [Student workbook](https://onedrive.live.com/view.aspx?resid=416406873CB120AB!3770&cid=416406873cb120ab&authkey=!ALunv1kXkaA0RLg&CT=1686689682662&OR=ItemsView) +* [Quick start video](https://youtu.be/fuqhcE6gXgI) +* [Quick start video (PowerPoint)](https://onedrive.live.com/view.aspx?resid=416406873CB120AB!3769&cid=416406873cb120ab&authkey=!ALunv1kXkaA0RLg&CT=1686689610440&OR=ItemsView) -#### [Unit 12 - Final Project](https://1drv.ms/f/s!AqsgsTyHBmRBhCl9zNQJnUEuOSX7) +#### [Unit 13 - Final Project](https://1drv.ms/f/s!AqsgsTyHBmRBhCl9zNQJnUEuOSX7) * [Classroom presentation](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!600&parId=416406873CB120AB!553&authkey=!AH3M1AmdQS45Jfs&app=PowerPoint) * [Educator Guide](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!598&parId=416406873CB120AB!553&authkey=!AH3M1AmdQS45Jfs&app=Word) * [Student workbook](https://onedrive.live.com/view.aspx?cid=416406873cb120ab&page=view&resid=416406873CB120AB!599&parId=416406873CB120AB!553&authkey=!AH3M1AmdQS45Jfs&app=Word) -* [Quick start video](https://onedrive.live.com/?authkey=%21AH3M1AmdQS45Jfs&cid=416406873CB120AB&id=416406873CB120AB%21601&parId=416406873CB120AB%21553&o=OneUp) +* [Quick start video](https://youtu.be/4A-npiseXG4) +* [Quick start video (PowerPoint)](https://onedrive.live.com/view.aspx?resid=416406873CB120AB!3773&cid=416406873cb120ab&authkey=!ALunv1kXkaA0RLg&CT=1686689876705&OR=ItemsView) ## Localization scripts -* [Video scripts](https://onedrive.live.com/?authkey=%21ALunv1kXkaA0RLg&id=416406873CB120AB%21523&cid=416406873CB120AB) \ No newline at end of file +* [Video scripts](https://onedrive.live.com/?authkey=%21ALunv1kXkaA0RLg&id=416406873CB120AB%21523&cid=416406873CB120AB) diff --git a/docs/courses/csintro.md b/docs/courses/csintro.md index 1fa620bc24a..e74139434a9 100644 --- a/docs/courses/csintro.md +++ b/docs/courses/csintro.md @@ -9,7 +9,8 @@ This course takes approximately 14 weeks to complete, spending about 1 week on e ![Space race image](/static/courses/csintro.jpg) ### ~ hint -**Download it** + +#### Download it The entire course is also available as a download or as a book. Choose any of these formats: @@ -38,10 +39,6 @@ Each of the 12 lessons is structured in this format: * Assessment - A project rubric and guidance for grading the project. * Standards - A list of CSTA K-12 Computer Science Standards and/or concepts covered by this lesson. -### Course on Flipgrid - -Flipcode for the **Intro to CS** course grid: **[csintromicrobit](https://flipgrid.com/csintromicrobit)** - ## Course contents * [About](/courses/csintro/about) @@ -52,14 +49,15 @@ Flipcode for the **Intro to CS** course grid: **[csintromicrobit](https://flipgr ### Lessons 1. [Making](/courses/csintro/making) -2. [Algorithms](/courses/csintro/algorithms) -3. [Variables](/courses/csintro/variables) +2. [Algorithms](/courses/csintro/algorithms) +3. [Variables](/courses/csintro/variables) 4. [Conditionals](/courses/csintro/conditionals) -5. [Iteration](/courses/csintro/iteration) +5. [Iteration](/courses/csintro/iteration) 6. [Review/Mini-Project](/courses/csintro/miniproject) 7. [Coordinate grid system](/courses/csintro/coordinates) 8. [Booleans](/courses/csintro/booleans) 9. [Bits, bytes, and binary](/courses/csintro/binary) 10. [Radio](/courses/csintro/radio) -11. [Arrays](/courses/csintro/arrays) -12. [Independent final project](/courses/csintro/finalproject) +11. [Accelerometer](/courses/csintro/accelerometer) +12. [Arrays](/courses/csintro/arrays) +13. [Independent final project](/courses/csintro/finalproject) diff --git a/docs/courses/csintro/SUMMARY.md b/docs/courses/csintro/SUMMARY.md index 21f3926a750..83057784840 100644 --- a/docs/courses/csintro/SUMMARY.md +++ b/docs/courses/csintro/SUMMARY.md @@ -107,6 +107,14 @@ * [Project](/courses/csintro/radio/project) * [Standards](/courses/csintro/radio/standards) +## [Accelerometer](/courses/csintro/accelerometer) + +* [Accelerometer](/courses/csintro/accelerometer) + * [Overview](/courses/csintro/accelerometer) + * [Activity](/courses/csintro/accelerometer) + * [Project](/courses/csintro/accelerometer) + * [Standards](/courses/csintro/accelerometer) + ## Arrays * [Arrays](/courses/csintro/arrays) diff --git a/docs/courses/csintro/about.md b/docs/courses/csintro/about.md index fbc76d15c74..8b9eade8d4b 100644 --- a/docs/courses/csintro/about.md +++ b/docs/courses/csintro/about.md @@ -7,4 +7,4 @@ You can follow him on Twitter at [@dkiang](http://twitter.com/dkiang). ![Mary Kiang](/static/courses/csintro/mary-kiang-foto.png) -Mary Kiang has been teaching for over twenty-five years at elementary, middle, and high school levels. She also developed curriculum in the Education Department of the Museum of Science in Boston. She currently teaches 6th grade Math/Science at Punahou School. Mary is a former programmer for Houghton Mifflin and Dun & Bradstreet and holds a Master’s degree in Elementary Education from Simmons College. Mary is the founder of GO Code!, an organization that supports girls and young women in exploring coding and STEM. +Mary Kiang has been teaching for over twenty-five years at elementary, middle, and high school levels. She also developed curriculum in the Education Department of the Museum of Science in Boston. She currently teaches 6th grade Math/Science at Punahou School. Mary is a former programmer for Houghton Mifflin and Dun & Bradstreet and holds a Master’s degree in Elementary Education from Simmons College. Mary is the founder of GO Code!, an organization that supports girls and young women in exploring coding and STEM. \ No newline at end of file diff --git a/docs/courses/csintro/accelerometer.md b/docs/courses/csintro/accelerometer.md new file mode 100644 index 00000000000..def595e554e --- /dev/null +++ b/docs/courses/csintro/accelerometer.md @@ -0,0 +1,32 @@ +# Accelerometer + +![Acceleration Example](/static/courses/csintro/accelerometer/highvelocitylowaccel.png) + +This unit introduces the accelerometer functionality of the micro:bit. Even if one is unfamiliar with the word “accelerometer,” most people are aware of the function. The micro:bit accelerometer measures how the micro:bit is positioned and moving through space. The unplugged activity has the students sense their own body’s way of knowing its position and movement through space. The birdhouse activity leads the students to use the micro:bit’s accelerometer capabilities to create a program that reminds them to stand up every so often. This unit’s project is to create a “multi-tool” that uses a combination of different sensors using the accelerometer to solve a problem or serve a purpose. + + +## Lesson objectives + +You will... + +* Understand how to use the Accelerometer blocks to sense the micro:bit’s position and movement in three-dimensional space +* Understand the x, y, z axes and measurement of gravitational force +* Apply the above knowledge and skills to design a unique program using the accelerometer + +## Lesson structure + +* Introduction: Understanding the Accelerometer +* micro:bit Activity: Marco Polo & Morse Code +* Project: Radio +* Assessment: Rubric +* Standards: Listed + +## Lesson plan + +1. [**Overview**: Understanding the Accelerometer](/courses/csintro/accelerometer/overview) +2. [**Activity**: Stand for Health](/courses/csintro/accelerometer/activity) +3. [**Project**: Accelerometer project](/courses/csintro/accelerometer/project) + +## Related standards + +[Targeted CSTA standards](/courses/csintro/accelerometer/standards) \ No newline at end of file diff --git a/docs/courses/csintro/accelerometer/activity.md b/docs/courses/csintro/accelerometer/activity.md new file mode 100644 index 00000000000..362ffaf2c43 --- /dev/null +++ b/docs/courses/csintro/accelerometer/activity.md @@ -0,0 +1,267 @@ +# Coding Activity: Stand for Health! + +In our modern world, most of us sit for long stretches of time without ever getting up to stretch our legs. Standing up every so often is good for our physical health. + +We will use the micro:bit’s accelerometer to create a program that will let us know if we have been sitting too long. + +1. Inside the Variables Toolbox: Create a variable to keep track of how much time has passed since the program started. + - Name the variable **TimeStarted**. + - Place the newly created ‘**set TimeStarted**’ block inside the ‘**on start**’ block. + - Set the value of this variable to **0 (zero)** each time the program starts. + +```block +let TimeStarted = 0 +``` + +2. From the Logic toolbox: Get and place an ‘**ifâ€Ļelse**’ block inside the ‘**forever**’ block. + - From the Logic toolbox: Get and place a **comparison** block into the ‘**true**’ space in the ‘**if**’ line of code. + - From the Input Toolbox: Get and place the **acceleration** block into the **left side of the comparison block**. + - In the **acceleration** block: Change the default value of ‘x’ to ‘z’. + - Set the **comparison** block symbol to ‘**less than**’ (<). + - Set the value on the **right-hand side of the comparison block** to **-700**. + +```block +basic.forever(function () { + if (input.acceleration(Dimension.Z) < -700) { + + } else { + + } +}) + +``` +## Check for Understanding + +This line of code tells the micro:bit to forever check the acceleration of the micro:bit and if the value of acceleration is less than -700, do something. + +Remember that when the micro:bit is lying flat on a surface with the screen pointing up: + + x is 0, y is 0, z is -1023, and strength is 1023. + +If the micro:bit is lying face up and flat, the Z value will be in this negative range. When your program is done, you can experiment with this number value, making it greater or less than the value used here to see how changing the value affects how the micro:bit reacts. + +3. From the Basic Toolbox: Get two ‘show leds’ blocks. + - Place one ‘show leds’ block under the ‘if’ line of code. + - Make an image of a chair in this block to indicate sitting. (Feel free to change it to an image that works for you.) + - Place the other ‘show leds’ block under the ‘else’ line of code. + - Make an image of a person standing in this block. (Feel free to change it to an image that works for you.) + +```block +basic.forever(function () { + if (input.acceleration(Dimension.Z) < -700) { + basic.showLeds(` + # . . . . + # . . . . + # # # # . + # . . # . + # . . # . + `) + } else { + basic.showLeds(` + . . # . . + # # # # # + . . # . . + . # . # . + . # . # . + `) + } +}) + +``` + +## Check for Understanding + +Now our micro:bit will show the chair image if the micro:bit is lying face up and flat, and a person standing image if the position of the micro:bit changes from lying flat and face up. + +Now, we will add in the time variable to keep track of how long we have been sitting. + +4. From the Logic toolbox: Get and place an ‘**if**’ block directly under the first ‘**show leds**’ block. + - From the Logic toolbox: Get and place a **comparison** block into the ‘**true**’ space in this ‘if’ line of code. + - From the Math Toolbox: Get and place an **operation** block into the **left side of the comparison block**. + - From the Inputâ€Ļmore Toolbox: Get and place a ‘**running time(ms)**’ block into the **left side of the math operation block**. + - Set the **math operation** to **minus (-)**. + - From the Variables Toolbox: Get and place a ‘**TimeStarted**’ block into the **right side of the math operations block**. + - Set the **comparison** block symbol to ‘**greater than**’ **(>)**. + - Set the value on the **right-hand side of the comparison block** to **10000** (ten seconds). + +```block +basic.forever(function () { + if (input.acceleration(Dimension.Z) < -700) { + let TimeStarted = 0 + basic.showLeds(` + # . . . . + # . . . . + # # # # . + # . . # . + # . . # . + `) + if (input.runningTime() - TimeStarted > 10000) { + + } + } else { + basic.showLeds(` + . . # . . + # # # # # + . . # . . + . # . # . + . # . # . + `) + } +}) +``` + +## Check for Understanding + +This if statement tells the micro:bit to check the difference between when we started the program and now. If the difference between when the program started and now is greater than 10 seconds, do something. + +We will have the micro:bit flash a message to indicate that you have been sitting longer than the desired time. + +NOTE: We’re using 10 seconds here for demonstration and testing purposes. In reality, you would want to set the time somewhere closer to half an hour, which is 1,800,000 milliseconds. + +5. From the Loops Toolbox: Get and place a ‘**repeat**’ block under the **if statement** we just created. + - From the Basic Toolbox: Get one ‘**show leds**’ block, one ‘**clear screen**’ block, and two ‘**pause**’ blocks. + - Place the ‘**show leds**’ block inside the ‘**repeat**’ block. + - Create an arrow image or an image of your choosing in this block. This image will flash on the micro:bit screen to alert the user that it’s time to stand up! + - Place one of the ‘**pause**’ blocks below this ‘**show leds**’ block. + - Place the ‘**clear screen**’ block just below the ‘**pause**’ block. + - Place the second ‘**pause**’ block just below the ‘**clear screen**’ block. + + + +```blocks +basic.forever(function () { + if (input.acceleration(Dimension.Z) < -700) { + let TimeStarted = 0 + basic.showLeds(` + # . . . . + # . . . . + # # # # . + # . . # . + # . . # . + `) + if (input.runningTime() - TimeStarted > 10000) { + for (let index = 0; index < 4; index++) { + basic.showLeds(` + . . # . . + . # # # . + # . # . # + . . # . . + . . # . . + `) + basic.pause(100) + basic.clearScreen() + basic.pause(100) + } + } + } else { + basic.showLeds(` + . . # . . + # # # # # + . . # . . + . # . # . + . # . # . + `) + basic.pause(100) + basic.clearScreen() + basic.pause(100) + } +}) +``` + +In addition to this flashing image, let’s add a message! + +6. From the Basic Toolbox: Get and place a ‘**show string**’ block below the ‘**repeat**’ loop block. + - Write a short message reminding the user to stand up! + - When the user stands up, they will see the person standing image. + - In case the user sits back down again, we need to reset the **TimeStarted** variable to the current program runtime. + - From the Variables Toolbox: Get and place a ‘**set TimeStarted**’ block just below the image of the person standing in the else section of the code. + - From the Inputâ€Ļmore Toolbox: Get and place a ‘**running time(ms)**’ block into the right side of the ‘**set TimeStarted**’ block. + +Note: You can also manually restart the program each time you sit back down by pressing the micro:bit’s reset button or by turning the battery pack on and off (if your battery pack has an on/off switch.) + + +## Complete program + +Here is the complete Stand Up! program: + +```blocks +let TimeStarted = 0 +basic.forever(function () { + if (input.acceleration(Dimension.Z) < -700) { + basic.showLeds(` + # . . . . + # . . . . + # # # # . + # . . # . + # . . # . + `) + if (input.runningTime() - TimeStarted > 10000) { + for (let index = 0; index < 4; index++) { + basic.showLeds(` + . . # . . + . # # # . + # . # . # + . . # . . + . . # . . + `) + basic.pause(100) + basic.clearScreen() + basic.pause(100) + } + basic.showString("Stand Up!!") + } + } else { + basic.showLeds(` + . . # . . + # # # # # + . . # . . + . # . # . + . # . # . + `) + basic.pause(100) + basic.clearScreen() + basic.pause(100) + TimeStarted = input.runningTime() + } +}) +``` + +Solution link: [https://makecode.microbit.org/S25983-43564-75646-69984]() + +## Knowledge Check + +**Questions:** + +1. What does velocity measure? +2. What does acceleration measure? +3. In the **Stand for Health!** activity, what did the TimeStarted block measure? +4. Using pseudocode, describe what these code blocks do. + +```blocks +basic.forever(function () { + if (input.acceleration(Dimension.X) < -700) { + basic.showLeds(` + # . . . . + # . . . . + # # # # . + # . . # . + # . . # . + `) + } else { + basic.showLeds(` + . . # . . + # # # # # + . . # . . + . # . # . + . # . # . + `) + } +}) +``` + +**Answers:** + +1. Velocity measures how fast an object’s position is changing over time, in both speed and direction. +2. Acceleration measures an object’s change in velocity. +3. The TimeStarted block measured the number of milliseconds that had passed since the start of the program. +4. If the micro:bit is lying flat, the screen will display a chair. If its position changes from lying flat, it will show a person standing. \ No newline at end of file diff --git a/docs/courses/csintro/accelerometer/overview.md b/docs/courses/csintro/accelerometer/overview.md new file mode 100644 index 00000000000..655004de866 --- /dev/null +++ b/docs/courses/csintro/accelerometer/overview.md @@ -0,0 +1,56 @@ +# Introduction + +An accelerometer is a device that measures acceleration. Acceleration is itself a measure of how an object’s velocity changes. +Velocity describes an object’s current speed in a particular direction. For example, you could describe the velocity of a car as “headed north at 50 mph”. + +![Acceleration Example](/static/courses/csintro/accelerometer/velocity.png) + +Acceleration is a measure of the rate at which the car’s speed and/or direction changes. +In all these cases an acceleration occurs: + +* **Direction change**: The car speed continues at 50 mph, yet the car turns to travel in a northeast direction +* **Speed change**: The car speed changes to 45 mph and the car continues heading north +* **Speed and direction change**: The car speed changes to 65 mph and the car turns to travel in a northeast direction + +**Acceleration** is **a measure of the rate of these changes over time**. + +Note that whether the car speeds up or slows down, an acceleration occurs. Acceleration doesn't only happen when something speeds up! + +![High Velocity Low Acceleration Example](/static/courses/csintro/accelerometer/highvelocitylowaccel.png) +![Low Velocity High Acceleration Example](/static/courses/csintro/accelerometer/lowvelocityhighaccel.png) + +You measure acceleration with the *milli-g*, which is **1/1000 of a g**. A *g* is as much acceleration as you get from Earth’s gravity. + +The micro:bit measures the acceleration value (*milli g-force*) in **one of three dimensions** or the combined force in **all directions (x, y, and z)**. + +When the micro:bit is flat on a table with the screen pointing up, the gravity force is aligned with the Z axis of the micro:bit. + +![Three Axes Illustration](/static/courses/csintro/accelerometer/lowvelocityhighaccel.png) + +If you tilt it up and down, the force will align with the Y axis; this is how we can detect tilting! As the force along Y grows, the micro:bit is tilting more and more vertically. + +### Parameters + +* **dimension**: The direction you are checking for acceleration or the total strength of force +* **x**: Acceleration in the left and right direction +* **y**: Acceleration in the forward and backward direction +* **z**: Acceleration in the up and down direction +* **strength**: the resulting strength of acceleration from all three dimensions (directions) + +The micro:bit’s accelerometer also measures how fast the micro:bit is speeding up or slowing down as it moves through space. + +### Output + +The accelerometer feature returns a number that represents the amount of acceleration. + +For example: When the micro:bit is lying flat on a surface with the screen pointing up: + +**x** is 0 +**y** is 0 +**z** is -1023 +**strength** is 1023 + +On the back of the micro:bit, look closely in the lower left corner for the accelerometer. + +![Accelerometer](/static/courses/csintro/accelerometer/accelerometer.png) + diff --git a/docs/courses/csintro/accelerometer/project.md b/docs/courses/csintro/accelerometer/project.md new file mode 100644 index 00000000000..11a51bdd62d --- /dev/null +++ b/docs/courses/csintro/accelerometer/project.md @@ -0,0 +1,90 @@ +# Project: Make an Accelerometer Project + +For this project, you will work separately or with a friend to design a project that incorporates the micro:bit's accelerometer capabilities. + +## Global Goals + +![UN Global Goals](/static/courses/csintro/accelerometer/global.png) + +In 2015, world leaders got together and came to agreement on 17 major global issues that need to be addressed. Visit the [Global Goals website](https://www.globalgoals.org) + +- Each of the 17 goals (globalgoals.org/goals) is broken down into smaller, more specific goals. +- The webpages for each of these more specific goals describe ways that citizens can take action to help reach that goal. + +After exploring the website, consider the following questions: + +- Which of the 17 goals interest you the most? +- Which of the 17 goals do you feel most directly affect you and your community? +- Why do you think they refer to these global issues as "goals to reach" rather than "problems to solve"? How does describing something as a "goal" differ from calling it a "problem"? + +## Global Goals and the micro:bit: Do Your :bit! + +As we have experienced already, the micro:bit is for making. From tools to games, you have made different projects and products using the micro:bit. The micro:bit can also be used to make a difference! + +### Do Your :bit + +![Do Your Bit Digital Challenge](/static/courses/csintro/accelerometer/bit.png) + +You can use your micro:bit to help reach one of the 17 Global Goals! + +Visit the [do your :bit](https://microbit.org/projects/do-your-bit) website to find out how other students from around the world have created projects that aim to help reach these goals. + +Explore the videos, resources, challenges, and projects to get ideas for a project of your own. + +### Project Expectations + +Follow the design thinking approach and make sure your project meets these specifications: + +- Uses the accelerometer to gather input about the micro:bit's position and motion in space +- Uses accelerometer blocks in a way that is integral to the program +- The program compiles and runs as intended and includes meaningful comments in the code +- Provide the written Reflection Diary entry (which we'll talk about after you complete your project) + +## Project Examples + +What follows are two complete project ideas. +Both can be found in the Coding Cards section on the home page of the [MakeCode for micro:bit](makecode.microbit.org) site. + +### Shake the Bottle + +![Shake the Bottle Instructions](/static/courses/csintro/accelerometer/shake.jpg) +  +This game is inspired by a game in the Nintendo Switch game 12Switch. Players take turns holding the micro:bit, which is taped to a bottle. They give it a shake and some bubbles show on the screen. They pass it to the next player, who also gives it a shake. At some point, the bottle will pop and the player left holding the bottle loses. + +There are no extra components needed for this game as it just uses the built-in accelerometer to detect when the bottle is shaken. + +#### The code + +We start with a variable called 'pop' and set its value to 0 at the start. When the first player shakes the micro:bit, it checks if the value of 'pop' is greater than 50. If it is over 50, then it displays an asterisk (*) and the game is over. If it is not over 50, it adds a random amount to 'pop' between 0 and 4 and shows some bubbles on display. + +_\*Credit: Twitter user [@stulowe80](https://twitter.com/stulowe80)_ + +### Zen + +![Shake the Bottle Instructions](/static/courses/csintro/accelerometer/shake.jpg) +  +ZEN is a game our students made that was inspired by the Nintendo Switch game 12Switch. The aim of the game is to strike a yoga pose and hold it while keeping the micro:bit completely still. If you wobble, your screen starts to fill up. If it is full, then you are out. + +There are no extra components needed for this game as it just uses the built-in accelerometer on the x-axis to detect how much it wobbles rotationally left and right. You could tape the micro:bit and battery to a piece of card to make it easier to hold flat. + + +#### The code + +We start by creating a variable called 'wobble' and setting it to 0 at the start. The value of 'wobble' is shown as a bar graph on the display. If 'wobble' becomes greater than 10, it shows an X and that player is out. If the accelerometer detects a tilt to the right or left exceeding 500 or -500, it adds 1 to your 'wobble' value. + +_\*Credit: Twitter user [@stulowe80](https://twitter.com/stulowe80)_ + +## Reflection + +Write a short reflection of about 150–300 words, addressing the following points: + +* What kind of project did you do? How did you decide what to pick? +* How does your project use the accelerometer? +* Describe something in your project that you are proud of. +* Describe a difficult point in the process of designing this program and explain how you resolved it. +* What feedback did your testers give you? How did that help you improve your design? +* How would you improve your project given more time? +* Relating to the pair programming process (if applicable): +* What challenges did you encounter working with a partner? +* What benefits did you gain? +* Publish your MakeCode program and include the link. diff --git a/docs/courses/csintro/accelerometer/standards.md b/docs/courses/csintro/accelerometer/standards.md new file mode 100644 index 00000000000..15a0c0e418c --- /dev/null +++ b/docs/courses/csintro/accelerometer/standards.md @@ -0,0 +1,7 @@ +# Standards + +## CSTA K-12 Computer Science Standards + +* 1B-AP-17 Describe choices made during program development using code comments, presentations, and demonstrations +* 2-AP-15 Seek and incorporate feedback from team members and users to refine a solution that meets user needs +* 2-DA-08 Collect data using computational tools and transform the data to make it more useful and reliable \ No newline at end of file diff --git a/docs/courses/csintro/algorithms.md b/docs/courses/csintro/algorithms.md index 69fc7adce99..2efd120b9d0 100644 --- a/docs/courses/csintro/algorithms.md +++ b/docs/courses/csintro/algorithms.md @@ -1,11 +1,11 @@ # Algorithms -This lesson introduces a conceptual framework for thinking of a computing device as something that uses code to process one or more inputs and send them to an output(s). +This unit introduces the four main components that make up a computer and the concept of **input** and **output** as it relates to programming the micro:bit. The coding activity starts with an explanation of pseudocode that leads to working with events and event handlers to program your micro:bit to make faces. The project incorporates all the new learning from this unit as you create your own fidget cube that responds to different inputs. ## Lesson objectives -Students will... +You will... -* Understand the four components that make up a computer and their functions. +* Understand the functions of the four components that make up a computer. * Understand that the micro:bit takes input, and after processing the input, produces output. * Learn the variety of different types of information the micro:bit takes in as input. * Apply this knowledge by creating a micro:bit program that takes input and produces an output. @@ -13,13 +13,8 @@ Students will... ## Lesson plan 1. [**Overview**: What is a computer and micro:bit hardware](/courses/csintro/algorithms/overview) -2. [**Unplugged**: What's your function?](/courses/csintro/algorithms/unplugged) -3. [**Activity**: Happy face, sad face](/courses/csintro/algorithms/activity) -4. [**Project**: Fidget cube](/courses/csintro/algorithms/project) - -## Flipgrid - -The [Flipgrid](https://info.flipgrid.com/) topic for the **Algorithms** lesson: https://flipgrid.com/31ed5382 +2. [**Activity**: Happy face, sad face](/courses/csintro/algorithms/activity) +3. [**Project**: Fidget cube](/courses/csintro/algorithms/project) ## Related standards diff --git a/docs/courses/csintro/algorithms/activity.md b/docs/courses/csintro/algorithms/activity.md index d014dbc13b9..92f42277125 100644 --- a/docs/courses/csintro/algorithms/activity.md +++ b/docs/courses/csintro/algorithms/activity.md @@ -1,20 +1,22 @@ # Activity: Happy Face, Sad Face -The micro:bit itself is considered hardware. It is a physical piece of technology. In order to make use of hardware, we need to write software (otherwise known as "code" or computer programs). The software "tells" the hardware what to do, and in what order to do it using algorithms. Algorithms are sets of computer instructions. +The micro:bit itself is considered hardware. It is a physical piece of technology. In order to make use of hardware, we need to write software (otherwise known as “code” or computer programs). The software “tells” the hardware what to do—and in what order to do it using algorithms. *Algorithms* are **sets of computer instructions**. -In this activity, we will discover how to use the micro:bit buttons as input devices, and write code that will make something happen on the screen as output. We will also learn about pseudocode, the MakeCode tool, event handlers, and commenting code. +In this activity, we will discover how to use the micro:bit buttons as input devices and write code that will make something happen on the screen as output. We will also learn about pseudocode, the MakeCode tool, event handlers, and commenting code. ## Pseudocode -What do you want your program to do? -The first step in writing a computer program is to create a plan for what you want your program to do. Write out a detailed step-by-step plan for your program. Your plan should include what type of information your program will receive, how this input will be processed, what output your program will create and how the output will be recorded or presented. Your writing does not need to be written in complete sentences, nor include actual code. This kind of detailed writing is known as pseudocode. Pseudocode is like a detailed outline or rough draft of your program. Pseudocode is a mix of natural language and code. +What do you want your program to do? The first step in writing a computer program is to create a plan for what you want your program to do. Write out a detailed step-by-step plan for your program. Your plan should include what type of information your program will receive, how this input will be processed, what output your program will create, and how the output will be recorded or presented. Your writing does not need to be written in complete sentences nor include actual code. This kind of detailed writing is known as *pseudocode*. Pseudocode is like a detailed outline or rough draft of your program. Pseudocode is a mix of natural language and code. For the program we will write, the pseudocode might look like this: + * Start with a blank screen * Whenever the user presses button A, display a happy face. * Whenever the user presses button B, display a sad face. ## Microsoft MakeCode -Now that you have a plan for your program, in the form of pseudocode, let's start creating the real program. In a browser window, open the Microsoft MakeCode for micro:bit tool (https://makecode.microbit.org). The MakeCode tool is called an IDE (Integrated Development Environment), and is a software application that contains everything a programmer needs to create, compile, run, test, and even debug a program. +Now that you have a plan for your program in the form of pseudocode, let’s start creating the real program in [Microsoft MakeCode](https://makecode.microbit.org/). Remember, the MakeCode tool is called an IDE (Integrated Development Environment) and is a software application that contains everything a programmer needs to create, compile, run, test, and even debug a program. + +1. In [Microsoft MakeCode](https://makecode.microbit.org/), start a new project. ## Tour of Microsoft MakeCode * Simulator - on the left side of the screen, you will see a virtual micro:bit that will show what your program will look like running on a micro:bit. This is helpful for debugging, and instant feedback on program execution. @@ -29,7 +31,7 @@ The features highlighted here are: 2. **Simulator** shows what your program will look like when running on a @boardname@ 3. **Hide** or **Show** the simulator pane 4. Program in either **Blocks** or **JavaScript** -5. Programming **Workspace** where you will build you program +5. Programming **Workspace** where you will build your program 6. Blocks **Toolbox** 7. **Download** your program to the @boardname@ 8. Name your project and **Save** it on your computer @@ -39,11 +41,9 @@ When you start a new project, there will be two blue blocks, ‘on start’ and In programming, an event is an action done by the user, such as pressing a key or clicking a mouse button. An event handler is a routine that responds to an event. A programmer can write code telling the computer what to do when an event occurs. -One fun unplugged activity you can do with kids to reinforce the idea of an action that waits for an event is the Crazy Conditionals activity. - Notes: -* Tooltips - Hover over any block until a hand icon appears and a small text box will pop up telling you what that block does. You can try this now with the ‘on start’ and ‘forever’ blocks. +* Tooltips - Hover over any block until a hand icon appears and a small text box will pop up telling you what that block does. You can try this now with the 'on start' and 'forever' blocks. >![Blocks tooltips](/static/courses/csintro/algorithms/blocks-tooltips.png) @@ -192,21 +192,21 @@ It is good practice to add comments to your code. Comments can be useful in a nu To comment a block of code: -* Right-click on the icon that appears before the words on a block. +* Right-click the code block. * A menu will pop up. Select ‘Add Comment’. ![Add comment menu](/static/courses/csintro/algorithms/add-comment.png) -* This will cause a question mark icon to appear to the left of the previous icon. -* Click on the question mark and a small yellow box will appear into which you can write your comment. +* This will cause a comment icon to appear to the left of the previous icon. +* Click on the comment icon and a small yellow box will appear into which you can write your comment. ![Write comment](/static/courses/csintro/algorithms/write-comment.png) -* Click on the question mark icon again to close the comment box when you are done. -* Click on the question mark icon whenever you want to see your comment again or to edit it. +* Click on the comment icon again to close the comment box when you are done. +* Click on the comment icon whenever you want to see your comment again or to edit it. Notes -* When you right-click on the icon that appears before the words on a block, notice that there are other options available to you that allow you to duplicate and delete blocks, as well as get help. Feel free to explore and use these as you code. +* When you right-click on the icon that appears before the words on a block, notice that there are other options available to you that allow you to duplicate, collapse, and delete blocks, as well as get help. Feel free to explore and use these as you code. * In JavaScript, you can add a comment by using two forward slashes, then typing your comment. The two forward slashes tell JavaScript that the following text (on that same line) is a comment. ```typescript @@ -248,6 +248,28 @@ input.onButtonPressed(Button.B, () => { basic.clearScreen() ``` +## Knowledge Check + +Questions: + +1. What is the difference between RAM and hard drive memory? + - a. RAM is the computer’s short-term memory and the hard drive is where the computer stores its long-term memory. + - b. The hard drive is where the computer stores its short-term memory and RAM is the computer’s long-term memory. + - c. RAM is used for programming and the hard drive memory is for storage. + - d. Hard drive memory is for storing files and RAM is used for processing inputs. + +2. What’s an algorithm? + - a. The word used to describe all computer codes + - b. Sets of instructions to a computer + - c. A type of hardware used with micro:bit + - d. The area of a MakeCode project that shows how a program looks when run on the micro:bit + +3. What is an event in programming? +4. What is an event handler? +Answers: - +1. The answer is a: RAM is the computer’s short-term memory and the hard drive is where the computer stores its long-term memory. +2. The answer is b: Sets of instructions to a computer +3. An action done by the user, such as pressing a key or clicking a mouse button +4. A routine that responds to an event diff --git a/docs/courses/csintro/algorithms/overview.md b/docs/courses/csintro/algorithms/overview.md index f73df5366a6..8c1a43986f2 100644 --- a/docs/courses/csintro/algorithms/overview.md +++ b/docs/courses/csintro/algorithms/overview.md @@ -1,7 +1,7 @@ # Introduction What is a micro:bit? -The micro:bit was created in 2015 in the UK by the BBC to teach computer science to students. The BBC gave away a micro:bit to every Year 7 student in the UK. You can think of a micro:bit as a mini computer. +The [micro:bit](www.microbit.org) was created in 2015 in the UK by the BBC and partners to teach computer science to students. The BBC gave away a micro:bit to every Year 7 (6th grade) student in the UK. You can think of a micro:bit as a mini computer. http://microbit.org ![BBC micro:bit](/static/courses/csintro/algorithms/bbc-microbit.jpg) @@ -11,31 +11,52 @@ There are 4 main components that make up any computer: ![Computer components](/static/courses/csintro/algorithms/cpu.png) -1. The Processor – this is usually a small chip inside the computer, and it’s how the computer processes and transforms information. Has anyone heard of the term “CPU”? CPU stands for Central Processing Unit. You can think of the processor as the Brains of the computer - the faster the processor, the more quickly the computer can think. +1. The Processor – this is usually a small chip inside the computer, and it’s how the computer processes and transforms information. Have you heard of the term “CPU”? CPU stands for Central Processing Unit. You can think of the processor as the brain of the computer - the faster the processor, the more quickly the computer can think. 2. The Memory – this is how the computer remembers things. There are two types of memory: ->* RAM (random access memory) - you can think of this as the computer’s short-term memory ->* Storage (also referred to as the “hard drive”) - this is the computer’s long-term memory, where it can store information even when power is turned off +>* RAM (random access memory) - You can think of this as the computer’s short-term memory. Things that are stored here will disappear when the computer is turned off. Can you think of examples of things that are stored in our short-term memory? Things that you forget after you go to sleep? +>* Storage (also referred to as the “hard drive”) - This is the computer’s long-term memory, where it can store information even when power is turned off. Can you think of examples of things that are stored in our long-term memory? Things we never forget? -3. Inputs – this is how a computer takes in information from the world.  On humans, our input comes in through our senses, such as our ears and eyes. What are some Computer Inputs?  Keyboard, Mouse, Touchscreen, Camera, Microphone, Game Controller, Scanner +3. Inputs – This is how a computer takes in information from the world. In humans, our input comes in through our senses, such as our ears and eyes. Computer Inputs might be things like: Keyboard, mouse, touchscreen, camera, microphone, game controller, scanner. Can you think of others? -4. Outputs – this is how a computer displays or communicates information.  On humans, we communicate information by using our mouths when we talk. What are some examples of communication that don't involve talking? Blushing, sign language. What are some examples of Computer outputs?  Monitor/Screen, Headphones/Speakers, Printer +4. Outputs – This is how a computer displays or communicates information. As humans, we communicate information by using our mouths when we talk. What are some examples of communication that don’t involve talking, like blushing or sign language? Some examples of computer outputs are: monitor/screen, headphones/speakers, printer. Can you think of others? -Now, let’s look at our micro:bit: +Now, let’s look at our micro:bit, using the [features page on the microbit website](http://microbit.org/guide/features/) as a visual aid. ![micro:bit hardware](/static/courses/csintro/algorithms/microbit-hardware.png) -* Use the [features page on the microbit website](http://microbit.org/guide/features/) as a visual aid -* Can you find the Processor? -* How much memory does the micro:bit have? 16K, which is smaller than many files on your computer! -* Can you locate the following Inputs?  Buttons (on board), Pins (at base), Accelerometer / Compass. ->Note: Though not pictured, the Light Sensor is located on the LED lights -* Where are the Outputs?  LED lights, Pins +See if you can answer the following questions: + +* Can you find the Processor? +* How much memory does the micro:bit have? +* Can you locate the following Inputs? Buttons, Pins, and Accelerometer/Compass +* Where are the Outputs? + +Answers: + +* Processor: On the back of the micro:bit, just below the Bluetooth and radio antenna +* Memory: 16K, which is smaller than many files on your computer! +* Inputs: Buttons (on board), Pins (at base) Note: Though not pictured, the Light Sensor is located on the LED lights. +* Outputs: LED lights, Pins All computers need electricity to power them.  There are 3 ways to power your micro:bit: -* Through the USB port at the top + +* Connecting the micro:bit to a computer through the USB port at the top * By connecting a battery pack to the battery connector * Through the 3V Pin at the bottom (not the recommended way to power your micro:bit) -On the top left corner you may notice that your micro:bit has a Bluetooth antenna.  This means your micro:bit can communicate and send information to other micro:bits. We will learn more about this feature in the Radio Lesson. +On the top left corner, you may notice that your micro:bit has a Bluetooth antenna. This means your micro:bit can communicate and send information to other micro:bits. We will learn more about this feature in Unit 10: Radio communication. + +## Knowledge Check + +Questions: + +1. What are the four main components that make up any computer? + +2. How many programmable buttons are on the micro:bit? + +Answers: + +1. The processor, the memory, the inputs and the outputs +2. Two \ No newline at end of file diff --git a/docs/courses/csintro/algorithms/project.md b/docs/courses/csintro/algorithms/project.md index 11c349ebe68..767b50bbeff 100644 --- a/docs/courses/csintro/algorithms/project.md +++ b/docs/courses/csintro/algorithms/project.md @@ -2,26 +2,27 @@ ![Sample fidget cube](/static/courses/csintro/algorithms/fidgetcube.jpg) -A fidget cube is a little cube with something different that you can manipulate on each surface. There are buttons, switches, and dials, and people who like to “fidget” find it relaxing to push, pull, press, and play with it. In this project, students are challenged to turn the micro:bit into their very own “fidget cube”. +A fidget cube is a little cube with something different that you can manipulate on each surface. There are buttons, switches, and dials, and people who like to “fidget” find it relaxing to push, pull, press, and play with it. In this project, you will be challenged to turn the micro:bit into your very own “fidget cube”. + +Here's an example of a fidget cube: -Show students some examples of fidget cubes: * Original Kickstarter Fidget Cube - [Fidget Cube: A Vinyl Desk Toy](https://www.kickstarter.com/projects/antsylabs/fidget-cube-a-vinyl-desk-toy) (there is a funny video showing the fidget cube in action). -## Discussion questions +Consider some of the following questions: -* Do any of your students fidget? -* What kinds of things do they fidget with? Spinning pens, fidget spinners, rings, coins? -* There are many different versions of fidget cubes available now. Do any students have any? -* Have they seen them before? -* What are the types of fidget activities? -* If students could add or modify features of the fidget cube, what would they choose to do? +* Do you fidget? How about someone you know? +* What kinds of things might that person fidget with? Spinning pens, fidget spinners, rings, coins? +* There are many different versions of fidget cubes available now. Have you seen them before? Do you have one? +* What are the types of fidget activities on these cubes? +* If you could add or modify features of the fidget cube, what would you choose to do? * What would make the ultimate fidget cube? -Remind students that a computing device has a number of inputs, and a number of outputs. The code that we write processes input by telling the micro:bit what to do when various events occur. +Remember that a computing device has a number of inputs, and a number of outputs. The code that we'll write in this project will process input by telling the micro:bit what to do when various events occur. ## Project -Make a fidget cube out of the micro:bit, create a unique output for each of the following inputs: +Make a fidget cube out of the micro:bit by creating a unique output for each of the following inputs: + * on button A pressed * on button B pressed * on button A+B pressed @@ -29,47 +30,8 @@ Make a fidget cube out of the micro:bit, create a unique output for each of the See if you can combine a maker element similar to what you created in Lesson 1 by providing a holder for the micro:bit that holds it securely when you press one of the buttons. -![](/static/courses/csintro/algorithms/fidget-cube.jpg) -Sample fidget cube designs +![Fidget cube](/static/courses/csintro/algorithms/fidget-cube.jpg) +_Sample fidget cube designs_ ## Project mod -* Add more inputs and more outputs - use more than 4 different types of input. Try to use other types of output (other than LEDs) such as sound! - -## Assessment - -**Competency scores**: 4, 3, 2, 1 - -### Inputs - -**4 =** At least 4 different inputs are successfully implemented.
-**3 =** At least 3 different inputs are successfully implemented.
-**2 =** At least 2 different inputs are successfully implemented.
-**1 =** Fewer than 2 different inputs are successfully implemented. - -### Outputs - -**4 =** At least 4 different outputs are successfully implemented.
-**3 =** At least 3 different outputs are successfully implemented.
-**2 =** At least 2 different outputs are successfully implemented.
-**1 =** Fewer than 2 different outputs are successfully implemented. - -### micro:bit program - -**4 =** micro:bit program:
-`*` uses event handlers in a way that is integral to the program
-`*` compiles and runs as intended
-`*` includes meaningful comments
-**3 =** micro:bit program lacks 1 of the required elements
-**2 =** micro:bit program lacks 2 of the required elements
-**1 =** micro:bit program lacks all of the required elements. - -### Collaboration reflection - -**4 =** Reflection piece includes:
-`*` brainstorming ideas
-`*` construction
-`*` programming
-`*` beta testing
-**3 =** Reflection piece lacks 1 of the required elements.
-**2 =** Reflection piece lacks 2 of the required elements.
-**1 =** Reflection piece lacks 3 of the required elements. +* Add more inputs and more outputs - use more than 4 different types of input. Try to use other types of output (other than LEDs) such as sound! \ No newline at end of file diff --git a/docs/courses/csintro/algorithms/unplugged.md b/docs/courses/csintro/algorithms/unplugged.md index d497a5e71ed..38708147e34 100644 --- a/docs/courses/csintro/algorithms/unplugged.md +++ b/docs/courses/csintro/algorithms/unplugged.md @@ -1,5 +1,7 @@ # Unplugged: What's your function & crazy conditionals +This is a classroom activity that teachers might choose to run with a classrom of students. + Materials * Pencils * Paper (or index cards) diff --git a/docs/courses/csintro/arrays.md b/docs/courses/csintro/arrays.md index e1ae3a0e003..975c071789e 100644 --- a/docs/courses/csintro/arrays.md +++ b/docs/courses/csintro/arrays.md @@ -5,7 +5,9 @@ This lesson introduces the fundamental concept of storing and retrieving data in an ordered fashion using Arrays. We'll also look at JavaScript as an alternate way of creating and modifying code. We'll look at the structure of a Melody as a list of notes.   ## Lesson objectives -Students will... + +You will... + * Explain the steps they would take to sort a series of numbers. * Recognize three common sorting algorithms. * Practice creating Arrays. @@ -14,8 +16,9 @@ Students will... * Demonstrate understanding and apply skills by creating a musical instrument that uses a micro:bit and a program that correctly and effectively uses Arrays to store data.   ## Lesson structure + * Introduction: Arrays -* Unplugged Activity: Different sorts of people +* Unplugged Activity: Different sorts * micro:bit Activity: Headband charades, Starry Starry Night * Project: Make a musical instrument * Assessment: Rubric @@ -24,14 +27,10 @@ Students will... ## Lesson plan 1. [**Overview**: Arrays](/courses/csintro/arrays/overview) -2. [**Unplugged**: Different sorts of people](/courses/csintro/arrays/unplugged) +2. [**Unplugged**: Different sorts](/courses/csintro/arrays/unplugged) 3. [**Activity**: Headband charades](/courses/csintro/arrays/activity) 4. [**Project**: Musical instrument ](/courses/csintro/arrays/project) -## Flipgrid - -The [Flipgrid](https://info.flipgrid.com/) topic for the **Arrays** lesson: https://flipgrid.com/f0f1bbc5 - ## Related standards [Targeted CSTA standards](/courses/csintro/arrays/standards) diff --git a/docs/courses/csintro/arrays/activity.md b/docs/courses/csintro/arrays/activity.md index 06561173036..676e1f5dcff 100644 --- a/docs/courses/csintro/arrays/activity.md +++ b/docs/courses/csintro/arrays/activity.md @@ -1,38 +1,50 @@ -## Activity: Headband charades and starry starry night - -![Starry Night](/static/courses/csintro/arrays/starry-night.png) +## Coding Activity 1: Headband charades Create an array of words that can be used as part of a charades-type game. -This activity is based on a very popular phone app invented by Ellen DeGeneres (https://bits.blogs.nytimes.com/2013/05/03/ellen-degeneres-iphone-game/). +This activity is based on a very popular phone app created by the producers of the [Ellen DeGeneres show](https://bits.blogs.nytimes.com/2013/05/03/ellen-degeneres-iphone-game/). ![Heads up game](/static/courses/csintro/arrays/headband-charades.png) -* From the Arrays Toolbox drawer, drag a 'set text_list to array of' block into the 'on start' block. -* You can use the variable name of text_list or rename it to something more meaningful like arrayWords. +### Set the arrayWords + +* In Microsoft MakeCode, start a new project and name it something like **'charades'**. Either delete the 'forever' block in the coding Workspace or move it to the side, as it's not used in the activity. +* From the Arrays Toolbox drawer, drag a **'set (text list0)'** block onto the Workspace and drop into the **'on start'** block. ```blocks let arrayWords = ["a", "b", "c"] ``` -Notice that the array comes with 2 string blocks. We’ll want more for our charades game. +Notice that the array comes with three string blocks. We'll want more for our charades game. -* Click on the **(+)** symbol at the end of the 'array of' block. -* Add as many values (elements) as you'd like to the array block by continuing to click on the **(+)**. -* For now, we’ll add 3 more values for a total of 6 values. +* Select the **(+)** symbol at the end of the **'array of'** oval block. Add as many values (elements) as you'd like to the array block by continuing to select the **(+)**. For now, we'll add four more values for a total of six values. ```blocks let arrayWords = ["a", "b", "c", "", "", ""] ``` -* Fill each string with one word. Choose words that will be fun for a game of charades. Example: + +* Fill each string with one word. Choose words that will be fun for a game of charades. For example: ```blocks let arrayWords = ["cat", "guitar", "flashlight", "cupcake", "tree", "frisbee"] ``` +### Code 'on (screen up)' + Now, we need a way to access one word at a time from this array of words. -* We can use the 'show string' block from the Basic Toolbox drawer, and the 'on screen up' event handler from the Input Toolbox drawer (this is a drop-down menu choice of the 'on shake' block) to tell the micro:bit to display a word when we tilt the micro:bit up. -* For this version, we’ll display the words one at a time in the order they were first placed into the array. -* We’ll use the index of the array to keep track of what word to display at any given time, so you'll need to create an 'index' variable. + +* We can use the **'show string'** block from the Basic Toolbox drawer and the **'on screen up'** event handler from the Input Toolbox drawer (this is a dropdown menu choice of the **'on shake'** block) to tell the micro:bit to display a word when we tilt the micro:bit up. + +For this version, we'll display the words one at a time in the order they were first placed into the array. + +* We'll use the index of the array to keep track of what word to display at any given time, so you'll need to create an **'index'** variable using the Make a Variable button in the Variables Toolbox drawer. + +Now, we need a way to access one word at a time from this array of words. + +* We can use the **'show string'** block from the Basic Toolbox drawer, and the **'on screen up'** event handler from the Input Toolbox drawer (this is a drop-down menu choice of the **'on shake'** block) to tell the micro:bit to display a word when we tilt the micro:bit up. + +For this version, we'll display the words one at a time in the order they were first placed into the array. + +* We'll use the index of the array to keep track of what word to display at any given time, so you'll need to create an **'index'** variable using the Make a Variable button in the Variables Toolbox drawer. ```block let arrayWords: string[] = [] @@ -42,12 +54,11 @@ input.onGesture(Gesture.ScreenUp, () => { }) ``` -* To start the game with the index at zero, add a 'set' variable block to the 'on' start block. -* Next, add the following: - ->* an image as a placeholder for when the program has started. Since charades is a guessing game, we made one that looks like a question mark (?), -* a countdown to the first word using show number blocks and pause blocks -* And show the first word in the array +* To start the game with the index at zero, add a 'set' variable block to the 'on start' block. +* Next, add the following: +>* An image as a placeholder for when the program has started. Since charades is a guessing game, we made one that looks like a question mark (?) +>* A countdown to the first word using show number blocks and pause blocks +>* And show the first word in the array ```blocks let index = 0 @@ -71,11 +82,11 @@ basic.showNumber(1) basic.showString(arrayWords[index]) ``` -So far we have a start to our game and a way to display the first word. +### Code 'on (screen down)' -Once that word has been guessed (or passed), we need a way to advance to the next word in the array. +So far, we have a start to our game and a way to display the first word. Once that word has been guessed (or passed), we need a way to advance to the next word in the array. -* We can do this by changing the index of the array with the 'on screen down' event handler from the Input Toolbox drawer (this is a drop-down menu choice of the 'on shake' block) to advance to the next word when we tilt the micro:bit down. +* We can do this by changing the index of the array with the **'on screen down'** event handler from the Input Toolbox drawer (this is a dropdown menu choice of the **'on shake'** block) to advance to the next word when we tilt the micro:bit down. ```block let index = 0 @@ -85,28 +96,36 @@ input.onGesture(Gesture.ScreenDown, () => { ``` We have a limited number of elements in our array, so to avoid an error, we need to check and make sure we are not already at the end of the array before we change the index. -  -* Under the Arrays Toolbox drawer, drag out a 'length of' block. The 'length of' block returns the number of items (elements) in an array. For our array, the length of block will return the value 6. -* But because computer programmers start counting at zero, the index of the final (6th) element is 5. -  + +* Under the Arrays Toolbox drawer, drag out a **'length of'** block. The **'length of'** block returns the number of items (elements) in an array. For our array, the length of block will return the value 6. But because computer programmers start counting at zero, the index of the final (6th) element is 5. + Some pseudocode for our algorithm logic: -* When the player places the micro:bit screen down: ->Check the current value of the index. ->> **If:** the current value of the index is less than the length of the array minus one (see **array bounds** note),
-**Then:** change the value of the index by one,
-**Else:** indicate that it is the end of the game. -### ~hint +When the player places the micro:bit screen down: + +* Check the current value of the index. +>* **If** the current value of the index is less than the length of the array minus one (see **array bounds** note), +>* **Then** change the value of the index by one, +>* **Else** indicate that it is the end of the game. -#### Array bounds +#### Note: Array bounds Our array has a length 6, so this will mean that as long as the current value of the index is less than 5, we will change the array by one. -Using ‘less than the length of the array minus one’ instead of the actual numbers for our array makes this code more flexible and easier to maintain. We can easily add more elements to our array and not have to worry about changing numbers elsewhere in the code. +Using **'less than the length of the array minus one'** instead of the actual numbers for our array makes this code more flexible and easier to maintain. We can easily add more elements to our array and not have to worry about changing numbers elsewhere in the code. -### ~  +#### Coding 'on (screen down)', continued -We can put this all together with an 'if...then...else' block and a 'less than' comparison block from the Logic Toolbox drawer, a subtraction block from the Math Toolbox drawer, and a 'game over' block from the Game Toolbox drawer (located under the Advanced menu). +* From the Logic Toolbox drawer, drag out an **'ifâ€Ļthenâ€Ļelse'** block onto the Workspace and drop it into the **'on screen down'** block. +* From the Logic Toolbox drawer, drag a **'0<0'** comparison block onto the Workspace and drop it into the **'ifâ€Ļthen'** clause replacing the default value of **'true'**. +* From the Variables Toolbox drawer, drag an **'index'** variable block onto the Workspace and drop it into the **first slot of the comparison block.** +* We need to check that the current index value is less than the length of the array minus one (the last index value). +>* From the Math Toolbox drawer, drag a **'0-0'** operator block onto the Workspace and drop it into the second slot of the comparison block. +>* From the Array Toolbox drawer, drag a 'length of array' block onto the Workspace and drop it into the first slot of the **'0-0'** math operator block. +>* In the **'length of array'** block, use the dropdown menu to select the **'text list'** array. +>* In the second slot of the math operator block, type 1. +>* Drag the **'change index'** block from below the **'ifâ€Ļthenâ€Ļelse'** block into the 'then' clause. +>* From the Game Toolbox drawer (under the Advanced Toolbox menu), scroll down to find the **'game over'** block. Drag it onto the Workspace and drop it into the **'else'** clause. ```blocks let index = 0 @@ -120,9 +139,11 @@ input.onGesture(Gesture.ScreenDown, () => { }) ``` -To make our game more polished, we’ll add 2 more blocks for smoother game play. +### Add some polish + +To make our game more polished, we'll add two more blocks for smoother game play. -* In case a word is already scrolling on the screen when a player places the micro:bit screen down, we can stop this animation and clear the screen for the next word by using a 'stop animation' block from the Led More Toolbox drawer, and a 'clear screen' block from the Basic More Toolbox drawer. +* In case a word is already scrolling on the screen when a player places the micro:bit screen down, we can stop this animation and clear the screen for the next word by using a **'stop animation'** block from the Led More Toolbox drawer, and a **'clear screen'** block from the Basic...More Toolbox drawer. ```blocks let index = 0 @@ -138,9 +159,10 @@ input.onGesture(Gesture.ScreenDown, () => { }) ``` -## Game Play +## Play the game -There are different ways you can play charades with our program. Here is one way you can play with a group of friends. +Download the program to the micro:bits and find someone to play it with. +There are different ways you can play charades with our program. Following is one way you can play with a group of friends. * With the micro:bit on and held so Player A cannot see the screen, another player starts the program to see the first word. * The other players act out this word charades-style for Player A to guess. @@ -188,21 +210,29 @@ basic.showNumber(1) basic.showString(arrayWords[index]) ``` +Solution link: [Headband Charades](https://makecode.microbit.org/_RMa7s7Rj9Cwv) + ![Random stars](/static/courses/csintro/arrays/starry-night.gif) ## Activity: Starry starry night In this micro:bit activity, we will create a set of random constellations on the micro:bit screen. We will use an array filled with numbers to tell us how many stars (dots) should be in each constellation. -Review the use of the random block in the Math category. +![Starry Night](/static/courses/csintro/arrays/starry-night.png) + +### Code random constellations + +* In MakeCode, start a new project and name it something like 'Starry night'. Either delete the 'forever' block in the coding Workspace or move it to the side as it's not used in the activity. + +Remember that the 'pick random' block in the Math Toolbox drawer will return a random value between a specified minimum and maximum value. We'll use this to plot a single dot at a random location on the screen by choosing a random number from 0 to 4 for the x axis and a random number from 0 to 4 for the y axis. -* Create a block that will plot a single dot at a random location on the screen by choosing a random number from 0 to 4 for the x axis and a random number from 0 to 4 for the y axis. +* From the Led Toolbox drawer, drag a **'plot'** block and connect it inside the 'on start' block. Then, from the Math Toolbox drawer, drop a **'pick random'** block into each of the x and y values and change the range in each from 0 to 4. ```blocks led.plot(randint(0, 4), randint(0, 4)) ``` -Next, let’s create a loop that will repeat the above code five times, for a constellation with five stars. +* Next, let's create a loop that will repeat the above code four times to create a constellation with four stars using the **'repeat'** block. To keep things simple, we won't check for duplicates, so it's possible you may end up with fewer than four visible stars. That's okay. ```blocks for (let index = 0; index <= 4; index++) { @@ -210,26 +240,19 @@ for (let index = 0; index <= 4; index++) { } ``` -Note that to keep things simple we don’t check for duplicates, so it’s possible you may end up with fewer than five visible stars. That’s okay. - -Next, let’s create an array with five numbers in it. We will loop through the array and create five separate constellations, using the numbers in the array to represent the number of stars in each of the five constellations. - -To create an array, you need to set the value of a variable to the array. The Arrays Toolbox has one already made for us. - -* From the Arrays Toolbox drawer, drag out the 'set list to' block that's attached to the ‘array of’ block containing numbers. -* Then click on the **(+)** symbol to add more values to the block, for a total of five. +### Code the array -You can drag additional numbers out of the Math category and snap them to the open slots in the Create array with block. Go ahead and change them to some random values, then attach the whole thing to the ‘on start’ event handler block. +Next, let's create an array with five numbers in it. We will loop through the array and create five separate constellations using the numbers in the array to represent the number of stars in each of the five constellations. -Now, when the micro:bit starts, it will create an array with those five values. Let’s create the constellations when the A button is pressed. Looking at our loop, instead of repeating 0 to 4 times, we actually want to use the value from the array to figure out how many stars to create. +* From the Arrays Toolbox drawer, drag out the 'set list' block onto the Workspace and drop it into the **'on start'** block. Then, select the (+) symbol three times to add more values to the array for a total of five. Type five random numbers from 0 to 25 in your array list since there are up to 25 LEDs on the micro:bit that could be used in your constellation. -* Drag the ‘list get value at’ block from the Arrays Toolbox drawer and replace the 4 with that block. +### Code the A button -You should see that there are more stars printed now, although there is an extra star; if the first value in the array is 5, you will actually see 6 stars because the loop runs when index is 0. +Let's activate our constellations when we press a button. -To fix this, we need to do a little math by subtracting 1 from whatever the value in the array is. You can use a Math operation block to do this. - -**Note:** Be sure to hit Refresh a few times on the simulator, because sometimes some stars get hidden behind other stars. +* From the Inputs Toolbox drawer, drag out an **'on button A pressed'** block onto the Workspace. +* Drag the **'repeat'** loop from the **'on start'** block into the **'on button A pressed'** block. Instead of repeating 4 times, let's use the values from the array to figure out how many stars to plot on our micro:bit. +* From the Arrays Toolbox drawer, drag a **'list get value at'** oval block onto the Workspace and drop into the **'repeat'** loop, replacing the default value of 4. When you type a 0, 1, 2, 3, or 4 into the **'get value'** block, it will plot the number of stars indicated by the value held in the specified array index. ```block let list = [5, 2, 1, 3, 4] @@ -239,9 +262,16 @@ for (let index = 0; index < list[0] - 1; index++) { } ``` -The above code takes the first value in the array and creates that many stars at random locations. Now, all we need to do is iterate through the entire array and do the same thing for each of the values. We will put the above code inside another loop that will run the same number of times as the length of the array (in this case, 5). You should also use a 'pause' block and a 'clear screen' block in between each constellation. +### Loop through the array + +Now, all we need to do is iterate through the entire array and do the same thing for each of the values. Instead of manually typing in values 0, 1, 2, 3, or 4 into the 'list get value at' block, let's use a variable and another loop to automatically increment the index each time. + +* From the Loops Toolbox drawer, drag another **'for loop'** block onto the Workspace and drop into the **'on button A pressed'** block around the existing **'repeat'** loop. +* From the Variables Toolbox drawer, drag an **'index'** variable block onto the Workspace and drop it into the **'list get value at'** block replacing the default 0 value. -Finally, you might attach the code that shows the constellations to an 'on button A pressed' event handler. +### Test in the simulator + +* Notice that if you try this in the simulator, all the stars will be plotted at once because the loops run too fast for us to see. So, we'll need to add a 'pause' block and a 'clear screen' block between each constellation. Here is the complete program. @@ -259,16 +289,22 @@ input.onButtonPressed(Button.A, () => { list = [5, 2, 1, 3, 4] ``` -## Traversing an array +Solution link: [Starry Night](https://makecode.microbit.org/_fJMWJPFoK8sH) + +## Coding Activity 3:Traversing an array -Traversing an array means proceeding through the elements of the array in sequence. Note that there is a special loop in the Loops Toolbox drawer called ‘for element value of list’. This loop automatically takes each element of your array in turn and copies it into a variable called value. +Traversing an array means proceeding through the elements of the array in sequence. You may have noticed that there is a special loop in the Loops Toolbox drawer called **'for element value of list'.** This loop automatically loops through each element of your array in turn to “traverse” through the array. ```block let list: number[] = [] for (let value of list) {} ``` -The following code is useful for printing out the values of the elements in your array: +* In Microsoft MakeCode, start a new project and name it something like **'traversing arrays'.** Either delete the 'forever' block in the coding Workspace or move it to the side as it's not used in the activity. + +### Complete code + +The following code is useful for displaying the values of the elements in your array: ```blocks let list = [5, 2, 1, 3, 4] @@ -281,16 +317,20 @@ input.onButtonPressed(Button.B, () => { }) ``` -However, note that value holds a copy of the element in the array, so changing value doesn’t affect the original element. +Solution link: [Fruit Array 2](https://makecode.microbit.org/_3draPYDEjWkj) -If you run the code below, then print out the array again, you will see that it is unchanged. +## Knowledge Check -```blocks -let list = [5, 2, 1, 3, 4] +**Questions:** -input.onButtonPressed(Button.A, () => { -   for (let value of list) { -       value += 1 -   } -}) -``` +1. How would you define the following terms? Array length, array sort, array index, array type +2. How are arrays different from variables? +3. Where do you find the Array blocks in MakeCode? +4. To create an array in MakeCode, what do you need to assign it to? + +**Answers:** + +1. **Array length**: The total number of items in the collection; **Array sort**: How you could order items in the collection (for example: date, price, name, color, and so on). Three common types of array sorts are: bubble, selection, and insertion; **Array index**: A unique address or location in the collection, e.g., page number in an album, shelf on a bookcase, etc.; **Array type**: The type of item being stored in the collection, e.g., comics, $1 coins, PokÊmon cards, numbers, strings, etc. +2. Variables are used to store a single value; An array can be used to store many values in one place; The information contained in an array is all similar; You can think of arrays like a list of items—like a row of mailboxes or a train of container boxes. +3. The Array blocks are found under the Advanced Toolbox menu in the Arrays category. +4. A variable \ No newline at end of file diff --git a/docs/courses/csintro/arrays/overview.md b/docs/courses/csintro/arrays/overview.md index f6d20b351ea..b6fb1dc9ad8 100644 --- a/docs/courses/csintro/arrays/overview.md +++ b/docs/courses/csintro/arrays/overview.md @@ -1,18 +1,18 @@ # Introduction -Any collector of coins, fossils, or baseball cards knows that at some point you need to have a way to organize everything so you can find things. For example, a rock collector might have a tray of specimens numbered like this: +Any collector of coins, fossils, or baseball cards knows that at some point you need to have a way to organize everything so you can find things. For example, a rock collector might have a tray of specimens numbered like this: ![Rock collection is an array](/static/courses/csintro/arrays/rock-collection.png) -Every rock in the collection needs its own storage space, and a unique address so you can find it later. -  -As your MakeCode programs get more and more complicated, and require more variables to keep track of things, you will want to find a way to store and organize all of your data. MakeCode provides a special category for just this purpose, called an Array. -  -* Arrays can store numbers, strings (words), or sprites. They can also store musical notes. -* Every spot in an array can be identified by its index, which is a number that corresponds to its location in the array. The first slot in an array is index 0, just like our rock collection above. -* The length of an array refers to the total number of items in the array, and the index of the last element in an array is always one less than its length (because the array numbering starts at zero.) +Every rock in the collection needs its own storage space and a unique address so you can find it later. + +As your MakeCode programs get more and more complicated and require more variables to keep track of things, you will want to find a way to store and organize all of your data. MakeCode provides a special category for just this purpose. It’s called an **array**, which is essentially just a list, or collection, of similar things. + +* Arrays can store numbers, strings (words), or sprites. They can also store musical notes. But they must store values of a similar type—an array cannot contain both numbers and words. +* Every spot in an array can be identified by its **index**, which is a number that corresponds to its location in the array. The first slot in an array is index 0, just like our rock collection pictured above. +* The length of an array refers to the total number of items in the array, and the index of the last element in an array is always one less than its length (because the array index numbering starts at zero.) So, in the Rock Collection above, the length of the array is 5 (it can hold 5 rocks), and the index of the last element is 4.   -In MakeCode, you can create an array by assigning it to a variable. The Array blocks can be found under the Advanced Toolbox menu. +In MakeCode, you can create an array by assigning it to a variable. The Array blocks can be found under the Advanced Toolbox menu. ![Arrays block menu](/static/courses/csintro/arrays/arrays-menu.png) @@ -30,30 +30,34 @@ let list = [4, 2, 5, 1, 3] input.onButtonPressed(Button.A, () => { basic.showNumber(list[0]) }) -```  +``` + The code above takes the first element in the array (the value at index 0) and shows it on the screen. -  + There are lots of other blocks in the Arrays Toolbox drawer. The next few Activities will introduce you to them.   -## Discussion +## Arrays in everyday life + +Do you collect anything? What is it? Comic books, cards, coins, stamps, books, etc. -* Ask your students if any of them collects anything. What is it? Comic books, cards, coins, stamps, books, etc. * How big is the collection? * How is it organized? * Are the items sorted in any way? * How would you go about finding a particular item in the collection? -  -In the discussion, see if you can explore the following array vocabulary words in the context of kids’ personal collections. -* Length: the total number of items in the collection -* Sort: Items in the collection are ordered by a particular attribute (e.g., date, price, name) -* Index: A unique address or location in the collection -* Type: The type of item being stored in the collection -  + +See if you can identify any examples of the following array vocabulary words using the context of your personal collections: + +* **Length:** the total number of items in the collection +* **Sort:** Items in the collection are ordered by a particular attribute (e.g., date, price, name) +* **Index:** A unique address or location in the collection +* **Type:** The type of item being stored in the collection + ## References -Once you start saving lots of different values in an array, you will probably want to have some way to sort those values. Many languages already implement a sorting algorithm that students can call upon as needed. However, understanding how those different sorting algorithms work is an important part of computer science, and as students go on to further study they will learn other algorithms, as well as their relative efficiency. +Once you start saving lots of different values in an array, you'll probably want to have some way to sort those values. Many languages already implement a sorting algorithm that students can call upon as needed. However, understanding how those different sorting algorithms work is an important part of computer science, and as you go on to further study, you'll learn about other algorithms, as well as their relative efficiency. There are some good array sorting videos: -* Visually displays a number of different types of sorts: https://www.youtube.com/watch?v=kPRA0W1kECg -* Bubble-sort with Hungarian folk dance: https://youtu.be/lyZQPjUT5B4 -* Insert-sort with Romanian folk dance: https://youtu.be/ROalU379l3U \ No newline at end of file + +* Visually displays a number of different types of sorts: [https://youtu.be/kPRA0W1kECg]() +* Bubble-sort with Hungarian folk dance: [https://youtu.be/lyZQPjUT5B4]() +* Insert-sort with Romanian folk dance: [https://youtu.be/ROalU379l3U]() \ No newline at end of file diff --git a/docs/courses/csintro/arrays/project.md b/docs/courses/csintro/arrays/project.md index b0f686e362c..0847329ba08 100644 --- a/docs/courses/csintro/arrays/project.md +++ b/docs/courses/csintro/arrays/project.md @@ -1,8 +1,8 @@ # Project: Musical instrument -This is a project in which students are challenged to create a musical instrument that uses arrays to store sequences of notes. The array of notes can be played when an input occurs, such as one of the buttons being pressed, or if one or more of the pins is activated. +This is a project in which you are challenged to create a musical instrument that uses arrays to store sequences of notes. The array of notes can be played when an input occurs, such as one of the buttons being pressed, or if one or more of the pins is activated.   -Ideally, the micro:bit should be mounted in some kind of housing, perhaps a guitar shape or a music box. Start by looking at different kinds of musical instruments to get a sense of what kind of shape you might want to build around your micro:bit. +Ideally, the micro:bit should be mounted in some kind of housing, perhaps a guitar shape or a music box. Start by looking at different kinds of musical instruments to get a sense of what kind of shape you might want to build around your micro:bit. ![micro:bit guitar](/static/courses/csintro/arrays/microbit-guitar.png) @@ -47,9 +47,10 @@ input.onButtonPressed(Button.B, () => { ``` ## Using arrays with musical notes -You can create an array of notes by attaching Music blocks to an array. Musical notes are described in words (e.g., Middle C, High C) but they are actually numbers. You can do Math operations on those numbers to change the pitch of your song. + +You can create an array of notes by attaching Music blocks to an array. Musical notes are described in words (e.g., Middle C, High C) but they are actually numbers. You can do Math operations on those numbers to change the pitch of your song.   -Here is an example of how to create an array with musical notes. Button A plays every note in the array. Button B plays the notes at twice the frequency (but doesn't alter the original notes.) +Here is an example of how to create an array with musical notes. Button A plays every note in the array. Button B plays the notes at twice the frequency (but doesn't alter the original notes.) ```blocks let list: number[] = [] @@ -69,7 +70,7 @@ input.onButtonPressed(Button.B, () => { list = [262, 392, 330, 392, 262] ``` -Remember that a 'for element value of list' loop makes a temporary copy of the value, so even if you change a value, it will not change the original element in the array. If students want to permanently change the values in their array (transpose music to increasingly higher keys, for example) they can use a for loop like this: +Remember that a 'for element value of list' loop makes a temporary copy of the value, so even if you change a value, it will not change the original element in the array. If you'd like to permanently change the values in your array (transpose music to increasingly higher keys, for example) you can use a for loop like this: ```blocks let list: number[] = [] @@ -81,7 +82,7 @@ input.onButtonPressed(Button.AB, () => { ``` ## Reflection -Have students write a reflection of about 150–300 words, addressing the following points: +Write a short reflection of about 150–300 words, addressing the following points: * Explain how you decided on your musical instrument. What brainstorming ideas did you come up with? * What properties does it share with a real musical instrument? What is unique? @@ -89,38 +90,4 @@ Have students write a reflection of about 150–300 words, addressing the follow * What was something that was surprising to you about the process of creating this program? * Describe a difficult point in the process of designing this program, and explain how you resolved it. * What feedback did your beta testers give you? How did that help you improve your musical instrument? - -## Assessment - -**Competency scores**: 4, 3, 2, 1 - -### Array - -**4 =** Stores and iterates through each element of the array successfully.
-**3 =** Stores each element of the array successfully.
-**2 =** Array skips values or has other problems with storing and/or retrieving elements.
-**1 =** Array doesn't work at all or no array present. - -### Maker component - -**4 =** Tangible component is tightly integrated with the micro:bit and each relies heavily on the other to make the project complete.
-**3 =** Tangible component is somewhat integrated with the micro:bit but is not essential.
-**2 =** Tangible component does not add to the functionality of the program.
-**1 =** No tangible component. - -### micro:bit program - -**4 =** The program:
-`*` Uses at least one array in a fully integrated and meaningful way
-`*` Compiles and runs as intended
-`*` Meaningful comments in code
-**3 =** Uses an array in a tangential way that is peripheral to function of project and/or program lacks 1 of the required elements.
-**2 =** Array is poorly implemented and/or peripheral to function of project, and/or lacks 2 of the required elements.
-**1 =** micro:bit program lacks 3 or more of the required elements. - -### Collaboration reflection - -**4 =** Reflection piece addresses all prompts.
-**3 =** Reflection piece lacks 1 of the required elements.
-**2 =** Reflection piece lacks 2 of the required elements.
-**1 =** Reflection piece lacks 3 of the required elements. +* Publish your MakeCode program and include the link. \ No newline at end of file diff --git a/docs/courses/csintro/arrays/unplugged.md b/docs/courses/csintro/arrays/unplugged.md index 0ee1740542c..ecae802d7c2 100644 --- a/docs/courses/csintro/arrays/unplugged.md +++ b/docs/courses/csintro/arrays/unplugged.md @@ -1,57 +1,69 @@ -## Unplugged: Different sorts of people +## Unplugged: Different sorts -In this activity, you will demonstrate different kinds of sorting methods on your own students. This is an unplugged activity, so your students will be standing at the front of the room. If you or your students are curious to see what these different sorts look like in code, we have included a MakeCode version of each algorithm in this lesson, for you to explore if you choose. +This activity asks you to carefully consider something that comes naturally to you: sorting objects.   ## Materials -* Sheets of paper numbered 1–10, one large printed number to a page - -## Set Up - -* Have up to ten students (the Sortees) stand up at the front of the classroom. Ask another student to volunteer to be the Sorter. -* Mix up the order of the papers and give each student a piece of paper with a number on it. They should hold the paper facing outward so their number is visible. Each of these students is like an element in an array. - -![Illustration of line of students representing an array](/static/courses/csintro/arrays/sorts-people.png) +* Pieces of paper numbered 1–10   ## Initial Sort -* Ask the Sorter to place the students in order by directing them to move, one at a time, to the proper place. -* Once the students are sorted, ask students the following: +* Mix up the order of the numbered pieces of paper. Then, put them in a line. +* Place the pieces in numberical order: but you must do this by moving **only one piece of paper at a time** to its proper place. +* Once the papers have been sorted, ask yourself the following: +>* How did you sort the papers into the right order? +>* Did you see a pattern? +>* What **exactly** did you do? ->*  How did she sort you into the right order? -* Did you see a pattern? -* What did she do? -  -Try to get students to be as precise as possible in explaining their thinking. Sometimes it helps to put the steps on the board, in an algorithm: -* _First, she went to the first student, then put him in the right place._ -* _Then she went to each of the next students and put them in the right place._ -  -Ask for clarification when necessary: _What does it mean when you say “put them in the right place”?_ -  -_To Put Someone in the Right Place:_ +Try to be as precise as possible in explaining their thinking. Sometimes it helps to write the steps down, as an algorithm: + +* _First, I went to the largest number, then put it in the right place._ +* _Then I went to each of the next largest numbers and put them in the right place._ + +Think about how you would explain this sorting process you just did to a computer, which can only understand and execute specific commands. -_Bring the person to the front of the line and then compare that person’s number with the first person’s number. If it’s larger, then move that person to the right. K eep doing this as long as the person’s number is larger than the person on the right._ -  ## Some Different Types of Sorts -In computer science, there are certain common strategies, or algorithms, for sorting a collection of values. Try acting out each of these different sorts with your students. +In computer science, there are certain common strategies, or algorithms, for sorting a collection of values. Try acting out each of these different sorts with the pieces of paper from earlier. We’ll demonstrate three sorting strategies: + +* Bubble sort +* Selection sort +* Insertion sort   ### Bubble Sort -Compare the first two students. If the student on the right is smaller than the student on the left, they should swap places. Then compare the second and third students. If the student on the right is smaller than the student on the left, they should swap places. When you reach the end, start over at the beginning again. Continue in this way until you make it through the entire row of students without swapping anybody. -  -#### In pseudocode: -1. Create a variable called counter. -2. Set the counter to zero. -3. Go through the entire array. -4. If the value you are considering is greater than the value to its right: ->1. Swap them ->2. Add one to counter -5. Repeat steps 2 through 4 as long as counter is greater than zero. +In a bubble sort, each consecutive pair of values is compared and the larger value is swapped to the right. As multiple passes over the array occur, the larger values “bubble” up towards one end, like bubbles in a fizzy pop. Because you have to compare the same pairs of numbers repeatedly, bubble sort is not terribly efficient—although it does have the advantage that if you make a complete pass comparing every consecutive pair and no swaps occur, you can preemptively declare the array sorted and your task is done. + +### Selection Sort +In a selection sort, multiple passes over the array are made to determine the smallest number on that pass. That smallest number is then swapped with the number on the end, and the next pass over the remaining unsorted numbers occurs. Because there is no way to tell if you have finished early, selection sort will always take the same number of passes as the number of elements in the array to complete. So, it is even slower than bubble sort, on average. + +### Insertion Sort +Imagine that we’re sorting a pile of papers alphabetically. We might place the top paper to the side, starting a new pile, and consider it sorted. Then, we would take the next paper and place it either before or after the previous paper in the sorted pile depending on whether that paper comes before or after it in the alphabet. Every subsequent paper that comes off the unsorted pile we would place right where it belongs in the sorted pile. That way, we only touch each paper once, and after one pass through the array, we are done. At first this seems more efficient than bubble sort and selection sort, but as the sorted pile grows larger, the number of comparisons we have to make to place each paper in the right place also increases. So, insertion sort actually isn’t any more efficient than the other two methods, although it is probably closest to the way a human being would sort an array of elements.   +### Bubble sort algorithm + +Follow these steps to demonstrate a bubble sort: + +1. Compare the first two papers. If the piece of paper on the right has a smaller number than the paper on the left, they should swap places. +2. Next, compare the second and third papers. If the paper on the right has a smaller number than the paper on the left, they should swap places. +3. When you reach the end, start at the beginning again. +4. Continue in this way until you make it through the entire row of numbers without swapping any of them. + ![Bubble Sort Animation](/static/courses/csintro/arrays/bubble-sort.gif) #### In MakeCode: -**Note:** Press B to display the array visually. The length of each vertical bar represents each number in the array, from left to right. Press A to sort the array using Bubble Sort. Press A + B to generate new random numbers for the array. +To code a bubble sort, the pseudocode could look like this: + +1. Create a variable called **counter**. +2. Set the counter to **0**. +3. Go through the entire array. +4. If the value you are considering is greater than the value to its right, swap them and add one to counter. +5. Repeat steps 2 through 4 as long as counter is greater than zero. + +Following is an example in MakeCode: + +* Press B to display the array visually. The length of each vertical bar represents each number in the array from left to right. +* Press A to sort the array using Bubble Sort. +* Press A + B to generate new random numbers for the array. ```blocks let temp = 0 @@ -108,19 +120,29 @@ list = [4, 2, 5, 1, 3] counter = 1 ``` -### Selection Sort -Take the first student on the left and consider that person’s number the smallest number you have found so far. If the next person in line has a number that is smaller than that number, then make that person’s number your new smallest number and continue in this way until you reach the end of the line of students. Then, move the person with the smallest number all the way to the left. Then start over from the second person in line. Keep going, finding the smallest number each time, and making that person the rightmost person in the sorted line of students. -  -#### In pseudocode: -1. Find the smallest unsorted value in the array. -2. Swap that value with the first unsorted value in the array. -3. Repeat steps 1 and 2 while the number of unsorted items is greater than zero. +### Selection Sort algorithm +Follow these steps to demonstrate a selection sort: + +1. Take the first paper on the left and consider that paper's number the smallest number you have found so far. +2. If the next paper in line has a number that is smaller than that number, make that paper's number your new smallest number and continue in this way until you reach the end of the line of papers. +3. Move the paper with the smallest number all the way to the left. +4. Start over from the second paper in line. +5. Keep going, finding the smallest number each time, and making that paper the rightmost paper in the sorted line of papers. ![Selection Sort Animation](/static/courses/csintro/arrays/selection-sort.gif) #### In MakeCode: -**Note:** The inner loop gets smaller as the sorting algorithm runs because the number of unsorted items decreases as you go. The index that the inner loop starts at needs to change as the number of sorted items increases, which is why we have to use a separate counter (item) and compute j every time through the inner loop. +To code a selection sort, the pseudocode could look like this: + +1. Find the smallest unsorted value in the array. +2. Swap that value with the first unsorted value in the array. +3. Repeat steps a and b while the number of unsorted items is greater than zero. + +Following is an example in MakeCode: + +* The inner loop gets smaller as the sorting algorithm runs because the number of unsorted items decreases as you go. +* The index that the inner loop starts at needs to change as the number of sorted items increases, which is why we have to use a separate counter (item) and compute j every time through the inner loop ```blocks let temp = 0 @@ -185,16 +207,21 @@ min = 1 ``` ### Insertion Sort -Take the first student on the left and consider that person sorted. Next, take the next student and compare him to the first student in the sorted section. If he is greater than the first student, then place him to the right of the student in the sorted section. Otherwise, place him to the left of the student in the sorted section. Continue down the line, considering each student in turn and then moving from left to right along the students in the sorted section until you find the proper place for each student to go, shifting the other students to the right to make room. -  -#### In pseudocode: +Follow these steps to demonstrate an insertion sort: + +1. Take the first paper on the left and consider that paper sorted. +2. Take the next paper and compare its number to the first paper in the sorted section. If its number is greater than the first paper's, then place it to the right of the paper in the sorted section. Otherwise, place it to the left of the paper in the sorted section. +3. Continue down the line, considering each paper in turn and then moving from left to right along the papers in the sorted section until you find the proper place for each paper to go, shifting the other papers to the right to make room. + +#### In MakeCode: + +To code an insertion sort, the pseudocode could look like this: + 1. For each element in the unsorted section of the list, compare it against each element in the sorted section of the list until you find its proper place. 2. Shift the other elements in the sorted list to the right to make room. 3. Insert the element into its proper place in the sorted list. -![Insertion Sort Animation](/static/courses/csintro/arrays/insertion-sort.gif) - -#### In MakeCode: +Following is an example in MakeCode: ```blocks let j = 0 @@ -250,6 +277,7 @@ j = 1 ``` ## Sidebar + In 2008, Illinois Senator Barack Obama was interviewed by Google’s CEO Eric Schmidt, who asks him a computer science interview question. Watch as the interview doesn’t go exactly as plannedâ€Ļ https://www.youtube.com/watch?v=k4RRi_ntQc8 diff --git a/docs/courses/csintro/binary.md b/docs/courses/csintro/binary.md index 22d4d30686c..9b39f2665f2 100644 --- a/docs/courses/csintro/binary.md +++ b/docs/courses/csintro/binary.md @@ -2,11 +2,12 @@ ![Binary numbers shown on a monitor](/static/courses/csintro/binary/binary-crt.png) -This lesson presents the concept of binary digits and base-2 notation. Students will learn how data is stored digitally and how it can be read and accessed. +This lesson presents the concept of binary digits and base-2 notation. You will learn how data is stored digitally and how it can be read and accessed. ## Lesson objectives -Students will... +You will... + * Understand what bits and bytes are and how they relate to computers and the way information is processed and stored. * Learn to count in Base-2 (binary) and translate numbers from Base-10 (decimal) to binary and decimal. * Apply the above knowledge and skills to create a unique program that uses binary counting as an integral part of the program. @@ -14,7 +15,6 @@ Students will... ## Lesson structure * Introduction: Bits and Bytes -* Unplugged Activity: Binary Vending Machine * micro:bit Activity: Binary Transmogrifier * Project: Make a Binary Cash Register * Assessment: Rubric @@ -23,13 +23,8 @@ Students will... ## Lesson plan 1. [**Overview**: Bits, bytes, binary](/courses/csintro/binary/overview) -2. [**Unplugged**: Binary vending machine](/courses/csintro/binary/unplugged) -3. [**Activity**: Binary transmogrifier](/courses/csintro/binary/activity) -4. [**Project**: Make binary a cash register](/courses/csintro/binary/project) - -## Flipgrid - -The [Flipgrid](https://info.flipgrid.com/) topic for the **Binary** lesson: https://flipgrid.com/d44cd204 +2. [**Activity**: Binary transmogrifier](/courses/csintro/binary/activity) +3. [**Project**: Make binary a cash register](/courses/csintro/binary/project) ## Related standards diff --git a/docs/courses/csintro/binary/activity.md b/docs/courses/csintro/binary/activity.md index f9eea274eab..b78a5f85de5 100644 --- a/docs/courses/csintro/binary/activity.md +++ b/docs/courses/csintro/binary/activity.md @@ -1,28 +1,32 @@ # Activity: Binary transmogrifier -Guide the students through building a binary transmogrifier (converter) that converts between binary (base-2) and decimal (base-10) numbers. Let them figure out a pattern that will allow them to do the conversion on the fly. +In this activity, you'll build a binary transmogrifier (converter) that converts between binary (base-2) and decimal (base-10) numbers. ![Transmogrifier cartoon](/static/courses/csintro/binary/transmogrifier.png) Calvin & Hobbes -Tell the students that they will be building a binary transmogrifier with the micro:bit. The user will be able to use the buttons to enter binary 0s and 1s and will be able to press A+B at any time to display the decimal equivalent of the number that has been entered. ## Create the Variables -Students will need to create a number variable to hold the running decimal total. -They should also create a string variable to hold the current binary number. -* From the Variables menu, make and name these two variables: decimal, binary. +First, you'll need to create a number variable to hold the running decimal total. +Then, you should create a string variable to hold the current binary number. + +* In Microsoft MakeCode, start a new project and name it something like **binary calculator** or **binary converter.** Either delete the **'forever'** block in the coding Workspace or move it to the side as it's not used in the activity. Then from the **Variables** menu, select the **Make a Variable** button to make two variables. Name one: **decimal** and the other: **binary** >![Make a variable](/static/courses/csintro/binary/make-a-variable.png) >![Name a variable](/static/courses/csintro/binary/name-a-variable.png) ## Initialize the Variables -When the program starts up, you should initialize your variables to starting values. -* `decimal` = `0` -* `binary` = `""` (empty string) -This also tells the micro:bit what type of variable it is. Use the empty string value found in the **Text** toolbox drawer, under the **Advanced** menu. + +When the program starts up, you should initialize your variables to starting values. + +* From the Variable Toolbox drawer, drag two 'set' blocks to the coding Workspace and drop them inside the 'on start' block. Depending on which variable you made first, the 'set' block will default to one of the new variables. Use the dropdown menu to set one block to binary and the other to decimal. + +Now, we can give these variables some starting values: For the decimal variable, keep the default parameter of 0. For the binary variable, we want an empty string to hold the binary text value. + +* Select the Advanced tab in the Toolbox to open up more Toolbox categories. Then select the Text Toolbox drawer to find the empty string oval block. Drag one onto the Workspace and drop it into the **'set binary to'** block replacing the 0. ![Select text on block menu](/static/courses/csintro/binary/select-text-blocks.png) @@ -31,15 +35,17 @@ let binary = "" let decimal = 0 ``` -By setting the binary variable to an initial value of “ “ you tell the micro:bit that it is a string variable: a literal string of characters. This is important because you will be adding to this string character by character. +By setting the binary variable to an initial value of " " you tell the micro:bit that it is a string variable: a literal string of characters. This is important because you will be adding to this string character by character. -## Transmogrify Me! -We are ready to start entering numbers. Remember that binary numbers are calculated based on the number of place values (“bits”) and as you enter 1s and 0s, the value changes. One way to calculate the decimal value is to wait until the user presses A+B, and then calculate the entire number based on the value of the string. +## Ready, set, calculate! -However, a much simpler method is to calculate the decimal number “on the fly”, which is to say, every time the user presses a 1 or a 0, calculate the current decimal value of that string so you only have to deal with one 0 or 1 at a time. +We are ready to start entering numbers. Remember that binary numbers are calculated based on the number of place values ("bits"), and as you enter 1s and 0s, the value changes. One way to calculate the decimal value is to wait until the user presses A+B, and then calculate the entire number based on the value of the string. -## What’s the Pattern? -This is a table of the first fourteen binary numbers and their decimal equivalents. Your goal is to use this table to figure out how to calculate a new correct decimal value based on whether a user enters a 0, or a 1 as the next number in the string. +However, a much simpler method is to calculate the decimal number "on the fly", which is to say, every time the user presses a 1 or a 0, calculate the current decimal value of that string so you only have to deal with one 0 or 1 at a time. + +## What's the pattern? + +This is a table of the first fifteen binary numbers and their decimal equivalents. Your goal is to use this table to figure out how to calculate a new correct decimal value based on whether a user enters a 0, or a 1, as the next number in the string ``` Binary Decimal Binary Decimal @@ -54,63 +60,52 @@ Binary Decimal Binary Decimal ``` For example, imagine you are the micro:bit. If the first number the human enters is a 1, you automatically know the new decimal value is a 1. If the second number that is entered is a 0, then your decimal value goes from 1 to 2. However, if the second number is also a 1, then your new decimal value goes from 1 to 3. -At that point, you either have a 10, or a 11 in your binary string. Let’s take 10 as an example. The decimal value of binary 10 is 2. If the third number entered is a 0, then your new decimal value goes from 2 to 4. If the third number entered is a 1, then your new decimal value goes from 2 to 5. +At that point, you either have a 10 or an 11 in your binary string. Let's take 10 as an example. The decimal value of binary 10 is 2. If the third number entered is a 0, then your new decimal value goes from 2 to 4. If the third number entered is a 1, then your new decimal value goes from 2 to 5. -If, on the other hand, you have 11 in your binary string, then your decimal value is 3. If the third number entered is a 0, then your new decimal value goes from 3 to 6. If the third number entered is a 1, then your new decimal value goes from 3 to 7. +If, on the other hand, you have 11 in your binary string, then your decimal value is 3. If the third number entered is a 0, then your new decimal value goes from 3 to 6. If the third number entered is a 1, then your new decimal value goes from 3 to 7. See if you can spot a pattern that will help you figure out, for any given decimal value, what the new decimal value should be if the user enters a 0, or if the user enters a 1. ![Binary number patterns](/static/courses/csintro/binary/binary-patterns.png) ## Pseudocode -Recall from our Algorithms lesson that it is a good idea to write out your algorithm in plain English, before you start coding in MakeCode. This is called pseudocode. The Input for this program will be the buttons. Try to write out what should happen when each of the buttons is pressed. -Here is one possible solution. Your own pseudocode might be different and that’s okay. +The input for this program will be the buttons. Try to write out what should happen when each of the buttons is pressed. Here is one possible solution. Your own pseudocode might be different and that's okay. Remember: there can always be different approaches in coding. When Button A is pressed: -1. Add a “1” to the end of the binary string. -2. Show the current value of the binary string. -3. Update the decimal value with the total. + +* Add a "1" to the end of the binary string. +* Show the current value of the binary string. +* Update the decimal value with the total. When Button B is pressed: -1. Add a “0” to the end of the binary string. -2. Show the current value of the binary string. -3. Update the decimal value with the total. + +* Add a "0" to the end of the binary string. +* Show the current value of the binary string. +* Update the decimal value with the total. When Buttons A+B are pressed: -1. Show the current value of the decimal string. -## Coding Steps -* From the Input Toolbox drawer, drag 3 of the ‘on button A pressed’ event handlers to your coding workspace -* Leave one block with button 'A’. Use the drop-down menus in the other 2 blocks to choose button ‘B’, and button ‘A+B’ +* Show the current value of the decimal string. -```block -input.onButtonPressed(Button.A, () => { - -}) -input.onButtonPressed(Button.B, () => { - -}) -input.onButtonPressed(Button.AB, () => { - -}) -``` +## Code button A + +* From the Input Toolbox drawer, drag three of the **'on button A pressed'** event handlers to your coding Workspace. Leave one block with button 'A'. Use the dropdown menus in the other two blocks to choose button 'B', and button 'A+B'. + +Let's work on what to do when button A is pressed. Button A represents a binary "1". Our first task is to join a "1" to the existing string variable called **binary**. -### ~ hint +* From the Variables Toolbox drawer, drag a **'set (variable)'** block onto the Workspace, and drop it into the 'on button A pressed' block. Then use the dropdown menu to select the 'binary' variable. +* From the Text Toolbox drawer (under the Advanced menu), drag the **'join'** block to your coding Workspace and drop it into the **'set binary to'** block replacing the default 0 value. +* From the Variables Toolbox drawer, drag a **'binary'** variable value block onto the Workspace and drop it into the first slot of the **'join'** block, replacing the default "Hello" +* In the second slot of the **'join'** block, replace the default value of "World" to **1**. This will append a "1" to whatever value is currently being held in the binary variable. -Buttons are on all kinds of electronic devices that we use. Did ever wonder how they actually work to signal an input event? +## Code button A display -https://www.youtube.com/watch?v=t_Qujjd_38o +Now, let's display this value on the micro:bit. -### ~ +* From the Basic Toolbox drawer, drag a 'show string' block onto the Workspace, and drop it after the 'set binary' block in the 'on button A pressed' block. +* From the Variables Toolbox drawer, drag a 'binary' variable value block onto the Workspace and drop it into the 'show string' block replacing the default value of "Hello!" -Let’s work on what to do when button A is pressed. -* Button A represents a binary “1”. Our first task is to join a “1” to the existing string variable called binary. -* From the Text Toolbox drawer (under the Advanced menu), drag the 'join' block to your programming workspace -* Next, use the 'set' variable block to assign the value of the 'binary' variable to the 'join' block -* Join the 'binary' variable and “1” by entering them into the appropriate slots in the 'join' block -* And show the binary value on the screen so that when users press a button they can see the entire binary string - ```block let binary = "" input.onButtonPressed(Button.A, () => { @@ -119,12 +114,21 @@ input.onButtonPressed(Button.A, () => { }) ``` -* Finally, you will need to update the current decimal value with the value of the entire binary string. This is pretty straightforward if you have been keeping track of the decimal value every time someone presses a button. The pattern is as follows: _(spoiler alert!)_ +Finally, you will need to update the current decimal value with the value of the entire binary string. The pattern pseudocode is as follows: ->* Whenever someone enters a 0, the new decimal value is twice the previous value. ->* If someone enters a 1, the new decimal value is twice the previous value, plus 1. +* Whenever someone enters a 0, the new decimal value is twice the previous value. +* If someone enters a 1, the new decimal value is twice the previous value, plus 1. -* For Button A, you will need to use the multiplication Math block and your binary variable block to create the proper formula. You will need to put that formula inside another Math addition block in order to add one to the result. +For button A (when a user enters a 1), you will need to use the **'multiplication'** Math block and your binary variable block to create the proper formula. You will need to put that formula inside another Math 'addition' block in order to add one to the result. + +* From the Variables Toolbox drawer, drag a **'set (variable)'** block onto the coding Workspace, and drop it into your **'on button A pressed'** block after the **'show string'** block. Then, use the dropdown menu to select the **'decimal'** variable. +* From the Math Toolbox drawer, drag out two blocks to the Workspace: the **multiplication** block and the **addition** block. Don't place these yet; just keep them on the Workspace (they should be greyed out). We'll be nesting the two math expressions. + +**Note:** When working the nested expressions—Math or Logic blocks placed inside each other—it's easier to do the block manipulation on the coding Workspace separately first before attaching to other blocks in your program. + +* From the Variables Toolbox drawer, drag a **'decimal'** variable value block onto the Workspace and drop it into the first slot of the multiplication math block replacing the default value of 0. Type a **2** into the second slot of the multiplication math block. Now, drag the multiplication math block into the first slot of the addition math block, replacing the default value of 0. +* Type a **1** into the second slot of the addition math block. Now, drag the whole Math expression into the **'set (decimal) to 0'** block to replace the 0 default value. +* Test this code in the Simulator to confirm it's working as intended. ```block let binary = "" @@ -136,10 +140,20 @@ input.onButtonPressed(Button.A, () => { }) ``` -* Your Button B algorithm is similar, although you will be joining a “0” to the binary variable and you are just multiplying the decimal variable by 2. -* Your Button A+B algorithm just uses a Show block to show the value of the decimal variable. +## Code button B + +Your button B algorithm is similar, although you will be joining a "0" to the binary variable, and you are just multiplying the decimal variable by 2. + +* An alternative to coding the blocks from the Toolbox drawers is to duplicate the entire **'on button A pressed'** set of blocks, then: +>* In the **'join binary 1'** block, change the 1 to **0**. +>* In the **'set decimal to'** block, select the 'decimal x 2' oval and pull it out of the blocks to "un-nest" it (it will be grayed out). Delete the '0 + 1' oval in the 'set decimal to' block and replace the resulting 0 value with the grayed out **'decimal x 2'** oval block. +* Again, test this new code in the Simulator to make sure it's working as intended. -Here is the completed program. +## Code button A+B + +Your button A+B algorithm just uses a **'show'** block to show the current value of the decimal variable. + +* From the Input Toolbox drawer, drag an **'on button A pressed'** block to the coding Workspace and use the dropdown menu to select **'A+B'**. Then, from the Basic Toolbox drawer, drag a **'show number'** block and drop it into the **'on button A+B pressed'** block. Then, duplicate a **'decimal'** variable value from one of the other blocks and replace the 0 of the **'show number'** block. ```blocks let binary = "" @@ -161,10 +175,41 @@ decimal = 0 binary = "" ``` +Solution link: [Binary Transmogrifier](https://makecode.microbit.org/_2CmVy1CcJLay) + ### Try it out! -Have someone else try your program out. Then think about how the program might be improved. + +Once you've tested all the code in the Simulator, download it to the micro:bit. Have someone else try your program out. Then, think about how the program might be improved. + +### Mod this! + Here are some additional modifications you might try: + * Add a way to clear the binary and decimal values so you can start over. * Add a way to erase the previous value. * Create a decimal-binary converter that allows you enter a decimal value and see the binary equivalent when you press A+B. +* Create a physical housing or case for your binary calculator! + +### All about buttons + +Buttons are on all kinds of electronic devices that we use. Have you ever wondered how they actually work to signal an input event? + +https://www.youtube.com/watch?v=iCHAIeoSpI4 + +## Knowledge Check + +**Questions:** + +1. What is the definition of a bit? +2. What is the definition of byte? +3. What is 37 in binary? +4. What is 110110 in decimal? +5. Put the following in order from smallest to largest measurement: Megabyte, Kilobyte, Terabyte, Gigabyte + +**Answers:** +1. A bit is a binary digit with two possible values: 0 or 1. +2. A byte is a sequence of binary digits made up of eight bits. It has 256 possible values from 00000000 through 11111111. +3. 100101 +4. 54 +5. Kilobyte, Megabyte, Gigabyte, Terabyte \ No newline at end of file diff --git a/docs/courses/csintro/binary/overview.md b/docs/courses/csintro/binary/overview.md index fef314e837e..671c547b237 100644 --- a/docs/courses/csintro/binary/overview.md +++ b/docs/courses/csintro/binary/overview.md @@ -1,18 +1,23 @@ # Introduction -Most everyone who uses a computer has heard the terms, kilobyte (kB), Megabyte (MB), Gigabyte (GB) and even Terabyte (TB), usually when referring to the size of computer files and hard drives as well as download speeds. Bandwidth or connection rates are measured in bits/second. But what is a bit and what is a byte and what do they have to do with computers? +Most everyone who uses a computer has heard the terms, Kilobyte (KB), Megabyte (MB), Gigabyte (GB) and even Terabyte (TB), usually when referring to the size of computer files and hard drives as well as download speeds. Bandwidth or connection rates are measured in bits/second. But what is a bit and what is a byte, and what do they have to do with computers? -Picture a basic room light. The light is either on or it is off. You control the current state of the light by flipping a switch that has only two settings, down (light off) and up (light on). The earliest computers used a series of mechanical switches to control the flow of electricity through their circuits, turning each one on or off. The on/off states of the circuits was used to represent and even store information. The smallest unit of information, representing the state of one switch, is known as a bit. +Picture a basic room light. The light is either on or off. You control the current state of the light by flipping a switch that has only two settings, on or off. The earliest computers used a series of mechanical switches to control the flow of electricity through their circuits, turning each one on or off. The on/off states of the circuits were used to represent and even store information. The smallest unit of information, representing the state of one switch, is known as a **bit**. -A bit is a binary digit and has only two possible values, zero or one. The value of the bit represents the current state of a single switch. If the switch is off, then the bit has the value zero. If the switch is on, then the bit has the value one. +A bit is a **binary** digit and has only two possible values, zero or one. The value of the bit represents the current state of a single switch. If the switch is off, then the bit has the value zero. If the switch is on, then the bit has the value one. -A bit can only represent two different values, zero or one. To represent larger pieces of information, bits are strung together in sequences of 8 called bytes. +A bit can only represent two different values, 0 or 1. To represent larger pieces of information, bits are strung together in sequences of eight called **bytes**. -A byte is a sequence of binary digits made up of 8 bits. +A byte is a sequence of binary digits made up of eight bits. -A byte can represent any value from 00000000 through 11111111, for a total of 256 different possible values. Each digit in a byte can be thought of as representing an individual switch that is either off (zero) or on (one). +A byte can represent any value from 00000000 through 11111111, for a total of 256 different possible values. Each digit in a byte can be thought of as representing an individual switch that is either off (0 or on (1). -Modern computers rely on transistors, which pack millions of tiny switches into a chip smaller than your thumb, but information is still represented in essentially the same way: as a series of ones and zeros. By using binary, computers can represent information simply and efficiently using a system that is very effectively modeled in digital circuitry. +Modern computers rely on transistors, which pack millions of tiny switches into a chip smaller than your thumb, but information is still represented in essentially the same way: as a series of 1s and 0s. By using **binary**, computers can represent information simply and efficiently using a system that is very effectively modeled in digital circuitry. + +In coding: + +* The 1s and 0s of bits and bytes can be used to represent letters, numbers, and even different keys on a computer keyboard. +* A bit can be used to hold a Boolean (true/false) value. A value of 0 represents “false” and a value of 1 represents “true.” ## Review @@ -26,5 +31,4 @@ Modern computers rely on transistors, which pack millions of tiny switches into ## Notes * The ones and zeros of bits and bytes can be used to represent letters, numbers, and even different keys on a computer keyboard. -* A bit can be used to hold a Boolean (true/false) value. A value of zero represents ‘false’ and a value of one represents ‘true’. - +* A bit can be used to hold a Boolean (true/false) value. A value of zero represents ‘false’ and a value of one represents ‘true’. \ No newline at end of file diff --git a/docs/courses/csintro/binary/project.md b/docs/courses/csintro/binary/project.md index 47318cd244f..ba9de322406 100644 --- a/docs/courses/csintro/binary/project.md +++ b/docs/courses/csintro/binary/project.md @@ -1,8 +1,6 @@ # Project: Make a binary cash register -The unplugged activity uses a vending machine as a model for creating different combinations of binary place values. We found that for n coins, there is one and only one way to make every number between 0 and 2^_n-1_. - -For this project, students should invent a paper and cardboard version of the binary counter, then program it to display the decimal value of those numbers. +For this project, you'll be making a binary cash register out of paper, cardboard, and of course the micro:bit. This will involve programming your cash register to display the decimal value of various binary numbers. Materials * Cardboard or heavy paper @@ -13,79 +11,57 @@ Materials * Duct tape ![micro:bit cash register](/static/courses/csintro/binary/microbit-cash-register.png) -Binary micro:bit Cash Register +_Binary micro:bit Cash Register_ + +## Project Example + +![Binary cash register project](/static/courses/csintro/binary/binary-cash-register.jpg) +_An implementation of the Binary Cash Register_ + +This is one possible design for a binary cash register. It uses coins and copper tape on a piece of cardboard. Normally, to indicate “off” or 0, the coins are flipped up. And to indicate “on” or 1, the coin is flipped so it lays flat across both pieces of copper tape, completing the circuit so the micro:bit can detect that that pin has been activated, and calculates and displays the decimal value of the binary number that is indicated by the coins. -## Tips -This is one possible design for a binary cash register. We used coins and copper tape on a piece of cardboard. Normally, the coins are flipped up (“off” or 0) and to indicate “on” or 1, the coin is flipped so it lays flat across both pieces of copper tape, completing the circuit so the micro:bit can detect that that pin has been activated, and calculates and displays the decimal value of the binary number that is indicated by the coins. +Copper tape is a thin, flexible strip of copper with an adhesive back. Usually, copper tape can conduct electricity even through the sticky side, but if you are sticking one piece of copper tape to another, be sure to go over the connection with your fingernail, pressing it down firmly. -Copper tape is a thin, flexible strip of copper with an adhesive back. You can sometimes find copper tape at the hardware, sold as slug tape, to keep slugs out of your garden. Usually, copper tape can conduct electricity even through the sticky side but if you are sticking one piece of copper tape to another, be sure to go over the connection with your fingernail, pressing it down firmly. +Because the micro:bit only has three pins/rings, this binary register is limited to three place values. You might use variables to represent each of the three place values, or you can simply keep a running total by adding the appropriate amount when each of the three pins is pressed. -Because the micro:bit only has three pins, this binary register is limited to three place values. Students might use variables to represent each of the three place values, or they can simply keep a running total by adding the appropriate amount when each of the three pins is pressed. +You will need to connect the ground (GND) pin using copper tape to the other side of the circuit – using the coin to connect them. You can stick the micro:bit into place using some sticky tape, or you can create an actual holder. The copper tape connections are delicate though, so be careful when plugging and unplugging the power cable from the board. -![Binary cash register project](/static/courses/csintro/binary/binary-cash-register.jpg) -An implementation of the Binary Cash Register +Project mod options + +## Mods for the binary cash register -## Extra mods -* Write some code that will display the number in binary when you press the A button. +* Write some code that will display the number in binary when you press the A button. * Think of a way to create more place values, perhaps by using a second micro:bit and a Radio connection. -## Optional project: Build a binary wristwatch -* Write a program that will display the correct time (once set) on the micro:bit. -* The 3-4 numbers displayed will be in binary (not decimal). -* To make the strap of the wristwatch, put 2 pieces of duct tape back-to-back, and use velcro tabs as the fasteners +## Optional additional project: Build a binary wristwatch + +Here's another idea for a project that deals with binary: + +* Write a program that will display the correct time (once set) on the micro:bit. +* The three to four numbers displayed will be in binary (not decimal). ![Binary wrist watch project](/static/courses/csintro/binary/binary-wrist-watch.jpg) -To make the strap of the wristwatch, you can put two pieces of duct tape back-to-back, and use Velcro tabs as the fasteners. +* To make the strap of the wristwatch, put two pieces of duct tape back-to-back, and use Velcro tabs as the fasteners. ![Holder](/static/courses/csintro/binary/microbit-holder.jpg) + This is a holder that allows the micro:bit to be worn on the wrist. ![Wooden structure to hold the micro:bit on the wrist](/static/courses/csintro/conditionals/microbit-holder.jpg) + This design supports the micro:bit in a rigid cradle and allows more delicate connections to the pins. ## Reflection -Have students write a reflection of about 150–300 words, addressing the following points: +Write a short reflection of about 150–300 words, addressing the following points: * Describe what the physical component of yur micro:bit project was (e.g., an armband, a cardboard mount, a holder, etc.) * How well did your prototype work? What were you happy with? What would you change? * What was something that was surprising to you about the process of creating this project? * Describe one way in which your project differed from the example that was given. How would you recognize it as your own? - -## Assessment - -**Competency scores**: 4, 3, 2, 1 - -### Binary display - -**4 =** All binary numerals display correctly.
-**3 =** At least 2 binary numerals display correctly.
-**2 =** At least 1 binary numeral displays.
-**1 =** No binary numerals display correctly. - -### micro:bit program - -**4 =** micro:bit program:
-`*` Uses binary in a way that is integral to the program
-`*` Uses mathematical operations to convert decimal-binary
-`*` Compiles and runs as intended
-`*` Meaningful comments in code
-**3 =** micro:bit program lacks 1 of the required elements.
-**2 =** micro:bit program lacks 2 of the required elements.
-**1 =** micro:bit program lacks 3 or more of the required elements. - -### Reflection - -**4 =** Reflection piece includes addresses all prompts.
-**3 =** Reflection piece lacks 1 of the required elements.
-**2 =** Reflection piece lacks 2 of the required elements.
-**1 =** Reflection piece lacks 3 of the required elements. - -## Additional questions to ponder -* How could you use a row of flashlights to represent a number to someone else far away? -* How might you use those flashlights to send a message? +* Publish your MakeCode program and include the link. ## Resources diff --git a/docs/courses/csintro/binary/unplugged.md b/docs/courses/csintro/binary/unplugged.md index 5e9c6c4495d..5c4deebd98c 100644 --- a/docs/courses/csintro/binary/unplugged.md +++ b/docs/courses/csintro/binary/unplugged.md @@ -135,6 +135,4 @@ Next, have the students use the above method in reverse to translate numbers fro **Examples:** >0 1 0 1 0 (_10_ )
-1 1 0 1 1 0 (_54_ ) - - +1 1 0 1 1 0 (_54_ ) \ No newline at end of file diff --git a/docs/courses/csintro/booleans.md b/docs/courses/csintro/booleans.md index 67a9ebd7c49..e91960d1b66 100644 --- a/docs/courses/csintro/booleans.md +++ b/docs/courses/csintro/booleans.md @@ -2,15 +2,16 @@ ![micro:bit Combo Box](/static/courses/csintro/booleans/cover.jpeg) -This lesson introduces the use of the boolean data type to control the flow of a program, keep track of state, and to include or exclude certain conditions. +This unit introduces the use of the **Boolean** data type to control the flow of a program, keep track of the status of the program, and to include or exclude certain conditions. In an unplugged activity, you will write pseudocode to simulate two coins being tossed at the same time. In the coding activity, you'll take the pseudocode from the unplugged activity and use it to code your micro:bit. In the project, you'll code your own unique program using Booleans and other blocks that you've explored and learned in the previous units. ## Lesson objectives -Students will... -* Understand what booleans and boolean operators are, and why and when to use them in a program. -* Learn how to create a boolean, set the boolean to an initial value, and change the value of the boolean within a micro:bit program. -* Learn how to use the random true or false block. -* Apply the above knowledge and skills to create a unique program that uses booleans and boolean operators as an integral part of the program. -  +You will... + +* Understand what Booleans and Boolean operators are, and why and when to use them in a program. +* Learn how to create a Boolean, set the boolean to an initial value, and change the value of the boolean within a micro:bit program. +* Learn how to use the random **true** or **false** block. +* Apply the above knowledge and skills to create a unique program that uses Booleans and Boolean operators as an integral part of the program. + ## Lesson structure * Introduction: Booleans in daily life * Unplugged Activity: Two Heads are Better Than One @@ -26,10 +27,6 @@ Students will... 3. [**Activity**: Double coin flipper](/courses/csintro/booleans/activity) 4. [**Project**: Boolean](/courses/csintro/booleans/project) -## Flipgrid - -The [Flipgrid](https://info.flipgrid.com/) topic for the **Booleans** lesson: https://flipgrid.com/36e0c7e0 - ## Related standards [Targeted CSTA standards](/courses/csintro/booleans/standards) \ No newline at end of file diff --git a/docs/courses/csintro/booleans/activity.md b/docs/courses/csintro/booleans/activity.md index dd4299449c7..08497f682d6 100644 --- a/docs/courses/csintro/booleans/activity.md +++ b/docs/courses/csintro/booleans/activity.md @@ -2,30 +2,31 @@ ![Example Board](/static/courses/csintro/booleans/fuzzies.jpg) -Guide the students to create a program using Boolean variables and operators. -We’ll use our pseudocode from the previous activity to code a double coin flipper program. -  -For the first step, let’s create our variables. -Make a variable for each of the following: -* `CoinAHeads` -* `CoinBHeads` -* `PlayerAScore` -* `PlayerBScore` -  -Now we need to initialize the variable values. -Put a 'set' variable block for each of these 4 variables inside the 'on start' block. -  -The initial value of a variable is the value the variable will hold each time the program starts. -By default: -* a string variable is initialized to an empty string `""` -* a number variable is initialized to `0` -* a Boolean is initialized to `false` -  -Initialize the number variables to zero and the Boolean variables to `false`. +Let's create a program using Boolean variables and operators, using the pseudocode from Lesson A to code a double coin flipper program. + +The pseudocode: + +* Use the random function to get a true/false value for Coin A. +* Use the random function to get a true/false value for Coin B. +* Compare the current values of Coin A and Coin B. +>* If the current true/false values of Coin A and Coin B are the same, add a point to Player A's score. +>* Otherwise, the current true/false values of Coin A and Coin B must be different, so add a point to Player B's score. +* When players are done with their double coin flipping, show the final scores for each player.   -You can find the false blocks under the Logic menu. +## Initialize the Variables -![Logic menu](/static/courses/csintro/booleans/logic-menu.png) +In Microsoft MakeCode, have students start a new project and name it something like: Double coin flipper. They can leave the 'on start' block in the coding Workspace but can delete the 'forever' loop block. + +* For the first step, let's create our variables. From the Variables Toolbox drawer, use the Make a variable button to create each of the following: +>* CoinAHeads +>* CoinBHeads +>* PlayerAScore +>* PlayerBScore +* Now, we need to initialize the variable values. Put four 'set' variable blocks inside the 'on start' block and use the drop-down menu to set the variable for each block to each of the new variables. +* The initial value of a variable is the value the variable will hold each time the program starts. By default: +>* a string variable is initialized to an empty string: "" +>* a number variable is initialized to: 0 +>* a Boolean is initialized to: false ```blocks let CoinAHeads = false @@ -41,22 +42,14 @@ basic.showLeds(` `) ``` -Notice that we also added an image for the start screen, so the user knows the program has started and is ready. Does the image look like two coins? +Leave the number variables at 0 and initialize the Boolean variables to 'false'. You can find the 'false' hexagon blocks under the Boolean section of the Logic Toolbox drawer. Then add an image for the start screen, so the user knows the program has started and is ready. In the example below, the image is intended to look like two coins.   ## Random coin flips -When the player shakes the micro:bit, we will code the micro:bit to give each of our Boolean variables a random true/false value. -  -* From the Input Toolbox drawer, drag an 'on shake' block to the coding workspace -* From the Variables Toolbox drawer, drag 2 'set' variable blocks to the coding workspace -* Drag the 2 'set' blocks into the 'on shake' block -* Change the default `item` to `CoinAHeads` and `CoinBHeads` -* From the Math Toolbox drawer, drag 2 'pick random true or false' blocks to the coding workspace -* Hover over this 'pick random' block and note that its pop-up description mentions coin flipping! -  -![Pick Math random boolean](/static/courses/csintro/booleans/math-random-boolean.png) +Let's use the micro:bit's *accelerometer* to mimic tossing a coin. The accelerometer measures the acceleration of your micro:bit; this component senses when the micro:bit is moved. It can also detect other actions like shake, tilt, and free fall. When the player shakes the micro:bit, we will code the micro:bit to give each of our Boolean variables a random true/false value. -* Attach these 'pick random' blocks to the 'set' variable blocks in the 'on shake' block +* From the Input Toolbox drawer, drag an **'on shake'** block to the coding Workspace. Drag the two **'Set CoinAHeads'** and **'Set CoinBHeads'** blocks from the **'on start'** block into the **'on shake'** block. +* From the Math Toolbox drawer, drag two 'pick random true or false' blocks to the coding Workspace. Hover over this 'pick random' block and note that its pop-up description mentions coin flipping! ```blocks let CoinBHeads = false @@ -68,9 +61,10 @@ input.onGesture(Gesture.Shake, () => { ``` Now that the virtual CoinA and CoinB have been virtually flipped, we need to compare the outcomes to see if they are the same or different. -  + * From the Logic Toolbox drawer, drag an 'if...then...else' block to the coding workspace * Drag the 'if...then...else' block into the 'on shake' block under the 'set' variable blocks +* Drop one each of these 'pick random' blocks to the 'set' variable blocks in the 'on shake' block ```blocks let CoinBHeads = false @@ -86,15 +80,24 @@ input.onGesture(Gesture.Shake, () => { }) ``` -Now our logic block is ready for the next steps of our pseudocode. -1. Compare the current values of Coin A and Coin B. -2. If the current true/false values of Coin A and Coin B are the same, add a point to Player A’s score. -3. Otherwise, if the current true/false values of Coin A and Coin B are different, add a point to Player B’s score. -  -Because we were able to visualize our blocks as we wrote our pseudocode, we already know what blocks we will use and also know that we have simplified our code as much as possible! -  -* We can now simply add this to our current code -* And provide user feedback by adding some visuals +That completes the first two steps of our pseudocode: + +* Use the random function to get a true/false value for Coin A. +* Use the random function to get a true/false value for Coin B. + +## Compare current values of both coins + +Now that the virtual CoinA and CoinB have been virtually flipped, we need to compare the outcomes to see if they are the same or different, which is the next step of our pseudocode: + +Compare the current values of Coin A and Coin B: If the current true/false values of Coin A and Coin B are the same, add a point to Player A's score. Otherwise, if the current true/false values of Coin A and Coin B are different, add a point to Player B's score. + +* From the Logic Toolbox drawer, drag an **'ifâ€Ļthenâ€Ļelse'** block to the coding Workspace. Then, drag the **'ifâ€Ļthenâ€Ļelse'** block into the **'on shake'** block under the **'set'** variable blocks. +* Because we were able to visualize our blocks as we wrote our pseudocode, we already know what blocks we will use and also that we have simplified our code as much as possible! We can now simply add this to our current code and provide user feedback by adding some visuals. +* From the Logic Toolbox drawer, in the Comparison section, drag a **'0 = 0'** hexagon block to the coding Workspace and replace the 'true' hexagon in the **'ifâ€Ļthenâ€Ļelse'** block. Then, from the Variables Toolbox drawer, drag a **'CoinAHeads'** variable to replace the first 0. You could drag the **'CoinBHeads'** variable to replace the second 0, but another option is to duplicate the **'CoinAHeads'** variable, snap it into the second 0, and use the drop-down menu to select **'CoinBHeads'** option. +* To add a point to Player A's score, go to the Variables Toolbox drawer and drag a **'change (variable) by 1'** block into the **'then'** option and make sure it's set to the **'PlayerAScore'** variable. Then to give a visual cue that Player A got the point, go to the Basic Toolbox drawer, drag a **'show leds'** block and **'pause'** block onto the coding Workspace and connect them into the **'then'** option above the **'change PlayerBScore by 1'** block. In the **'show leds'** block, select the boxes to show the letter A. +* Do the same for Player B in the 'else' option. Instead of dragging the needed blocks from the Toolbox drawers, duplicate the blocks and change the 'show leds' block and 'change PlayerAScore' variable accordingly. +* To signify the end of the on-shake coin toss, let's show the two-coin image on the micro:bit again. From the Basic Toolbox drawer, drag a **'show leds'** block to the coding Workspace and connect it below the conditional blocks. Select the boxes to show the two coins. +* Now, test the code in the Simulator to make sure it works as intended. ```blocks let PlayerBScore = 0 @@ -135,7 +138,18 @@ input.onGesture(Gesture.Shake, () => { }) ``` -To finish our program, we’ll display the players’ current scores on button A pressed. +## Show each player's score + +To finish our program, we'll complete the last step of the pseudocode. + +When players are done with their double coin flipping, show the final scores for each player. + +We'll use button A to do this. + +* From the Input Toolbox drawer, drag an 'on button A pressed' block to the coding Workspace. Duplicate the 'show leds' block with the letter A and connect it inside the 'on button A pressed' block. Then, from the Basic Toolbox drawer, drag the 'show number' block to the Workspace and connect it below the 'show leds' block. +* From the Variables Toolbox drawer, drag a 'PlayerAScore' variable to replace the 0. +* Let's add a pause before showing Player B's score. Use the drop-down menu and select 500 ms. Then, follow the previous steps to show Player B's score. Try to complete this without going into the Toolbox! Hint: Use duplicates and drop-down menus. +* The final steps are to add another 500 ms pause and have the micro:bit show the double coins to signify it's ready for another on-shake coin toss. Here is the complete program for our Double Coin Flipper. @@ -145,21 +159,13 @@ let PlayerAScore = 0 let CoinBHeads = false let CoinAHeads = false input.onButtonPressed(Button.A, () => { - CoinAHeads = Math.randomBoolean() - CoinBHeads = Math.randomBoolean() - PlayerAScore = 0 - PlayerBScore = 0 - basic.showLeds(` - . # . . . - # # # . . - . # . # . - . . # # # - . . . # . - `) + basic.showString("A:" + PlayerAScore) + basic.pause(100) + basic.showString("B:" + PlayerAScore) }) input.onGesture(Gesture.Shake, () => { - CoinAHeads = true - CoinBHeads = true + CoinAHeads = Math.randomBoolean() + CoinBHeads = Math.randomBoolean() if (CoinAHeads == CoinBHeads) { basic.showLeds(` . . # . . @@ -189,25 +195,24 @@ input.onGesture(Gesture.Shake, () => { . . . # . `) }) -```  +``` -Try it out! -Have the students play a few more rounds of the Double Coin Flip using their new micro:bit Double Coin Flipper! -  -## Boolean operator NOT in a Loop +Solution link: [Random Coin Toss](https:/makecode.microbit.org/_YHuAxKere6vM) -```block -input.onGesture(Gesture.Shake, () => { - while (!(input.buttonIsPressed(Button.A))) { - for (let i = 0; i < 2; i++) { - music.playTone(262, music.beat(BeatFraction.Half)) - music.playTone(523, music.beat(BeatFraction.Half)) - } - } -}) -``` +## Test, Download, and Play! Try it out! + +Test your code on the Simulator. Then, download to the micro:bit and try it out! Play a few more rounds of the Double Coin Flip using your new micro:bit Double Coin Flipper! + +## Knowledge Check + +Questions: + +1. How many values can a Boolean have? +2. Name the three common Boolean operators we have discussed in this unit? +3. Why do we set the initial value of a variable inside the **'on start'** block? -Do you remember this code from our micro:bit Alarm? -Can you read this code and tell what it does? +Answers: -_If the micro:bit is shaken, the micro:bit will play two tones twice and keep repeating this action until button A is pressed. So, after shaking, as long as ‘is button A pressed?’ is false, the two tone alarm will continue to repeat._ +1. A Boolean data type has only two values: true or false. +2. And, Or, Not +3. The initial value of a variable is the value the variable will hold each time the program starts. \ No newline at end of file diff --git a/docs/courses/csintro/booleans/overview.md b/docs/courses/csintro/booleans/overview.md index 2ad4b95869a..9eb1e9dcbd6 100644 --- a/docs/courses/csintro/booleans/overview.md +++ b/docs/courses/csintro/booleans/overview.md @@ -1,73 +1,74 @@ # Introduction There are several different data types used in computer programming. We have already used two of these types: -* [String](/types/string) (for text) -* [Integer](/types/number) (for numbers) -Boolean is another type of data. A boolean data type has only two values: true or false. -In true binary fashion, these two values can be represented by the numbers 1 = true, and 0 = false. -  -Booleans are useful in programming for decision-making, often deciding when certain functions and parts of programs should start or stop running and are also used in database searches. -  -Ask the students to think of things in daily life that have only two values or states. The status is always one value or the other value. -  -Examples of Booleans in daily life +* [String](/types/string) (for text and alphanumeric characters) +* [Integer](/types/number) (for integer and decimal values) + +**Boolean** is another type of data. A Boolean data type has only two values: **true** or **false**. In true binary fashion, these two values can be represented by the numbers: 1 = true, and 0 = false. + +Booleans are useful in programming for decision making, often deciding when certain functions and parts of programs should start or stop running. They’re also used in database searches. + +Can you think of things in your daily life that have only two values or states? The status must always be one value or the other value. + +Examples of Booleans in daily life: + * Lights: On or Off * Time: AM or PM -* You!: Asleep or Awake +* You: Asleep or Awake * Weather: Raining or Not Raining * Math: Equal to or Not Equal to * Game: Truth or Dare * Soda: Coke or Pepsi * At the store: Paper or Plastic? Cash or Credit? Chip or Swipe?   -Note: -Arguments can be made that some of these can have more than two values. -For example: At the store, you may have brought your own reusable bags or pay by check. -Let the students discuss these to help them hone in on which examples best represent Booleans. +>**Note:** Arguments could be made that some of these can have more than two values. For example: At the store, you may have brought your own reusable bags or pay by check. Which of these examples best represent Booleans? -A student might argue that a dimmer switch on a light or the brightness value on the micro:bit LEDs allow the lights to be in a state between on and off. One could respond that you can classify ‘on’ as the state where any electricity at all is running through the bulb (on) versus no electricity at all (off). -  -In programming, if you have worked with conditionals or loops, you have already worked with this type of logic: -* If a certain condition is true, do this, otherwise (if condition is false), do something else. -* While a certain condition is true, do this -  -Boolean Operators: AND, OR, and NOT -To make working with Booleans useful for solving more complex decisions and searches, we can connect two or more Booleans into one decision statement. To do this, we use what are known as Boolean operators. The three most common and the ones we will use with the micro:bit are And, Or, and Not. +In programming, if you have worked with conditionals or loops, you have already worked with this type of logic, just like we’ve done in previous units (Unit 4: Conditionals and Unit 5: Iteration). + +* If a certain condition is true, do this; otherwise (if condition is false), do something else. +* While a certain condition is true, do this. + +## Boolean Operators: AND, OR, and NOT + +To make working with Booleans useful for solving more complex decisions and searches, we can connect two or more Booleans into one decision statement. To do this, we use what are known as **Boolean operators.** The three most common and the ones we will use with the micro:bit are **And, Or,** and **Not.** These operators can be used in conditionals and loops, like so: + * If condition A is true AND condition B is true * If condition A is true OR condition B is true * While event A has NOT happened   Let’s look at how each of these work. -## AND -(Condition A AND Condition B) -For this expression to evaluate as true, both conditions in the expression need to be true. -So, if both Condition A AND Condition B are true, the expression will evaluate as or return true. -  -## OR -(Condition A OR Condition B) -For this expression to evaluate as true, only one of the conditions in the expression needs to be true. -If Condition A is true, the expression will return true regardless of whether Condition B is true or false. -If Condition B is true, the expression will return true regardless of whether Condition A is true or false. -  -## NOT -NOT can be used when checking that a condition is false (or not true). -For example: +### AND + +(Condition A AND Condition B): For this expression to evaluate as true, both conditions in the expression need to be true. So, if both Condition A AND Condition B are true, the expression will evaluate as (or return) true. + +### OR + +(Condition A OR Condition B): For this expression to evaluate as true, only one of the conditions in the expression needs to be true. If Condition A is true, the expression will return true regardless of whether Condition B is true or false. If Condition B is true, the expression will return as true regardless of whether Condition A is true or false. + +### NOT + +NOT can be used when checking that a condition is false (or not true). For example: + * (NOT Condition A and Condition B) evaluates as true only if Condition A is false and Condition B is true. * (Condition A and NOT Condition B) evaluates as true only if Condition A is true and Condition B is false. * (NOT Condition A and NOT Condition B) evaluates as true only if both Condition A and Condition B are true. -NOT is also useful when using a loop. For example, you can use a NOT to check   -While button A is NOT pressed, continue to run this codeâ€Ļ -  -Note: ‘False’ can be thought of as equivalent to ‘NOT true’. + +NOT is also useful when using a loop. For example, you can use a NOT to check: + +* While button A is NOT pressed, continue to run this codeâ€Ļ + +>**Note:** “False” can be thought of as equivalent to “NOT true”. ## Sidebar material + ![George Boole](/static/courses/csintro/booleans/george-boole.jpg) -Image credit: Wikimedia Commons + +_Image credit: Wikimedia Commons_ George Boole (2 November 1815 – 8 December 1864) was an English mathematician, educator, philosopher and **logician**. He worked in the fields of differential equations and algebraic **logic**, and is best known as the author of The Laws of Thought (1854) which contains **Boolean** algebra. diff --git a/docs/courses/csintro/booleans/project.md b/docs/courses/csintro/booleans/project.md index d3e51a8698f..066c7d882a1 100644 --- a/docs/courses/csintro/booleans/project.md +++ b/docs/courses/csintro/booleans/project.md @@ -2,21 +2,56 @@ ![Two-Player Game Example Board](/static/courses/csintro/booleans/two-player.jpg) -This is an assignment for students to come up with a micro:bit program that uses Boolean variables, Boolean operators, and possibly the random function. +In this project, you will come up with a micro:bit program that uses Boolean variables, Boolean operators, and possibly the random function.   +## Project Expectations + +Follow the design thinking approach and make sure your project meets these specifications: + +* More than two Boolean variables are implemented in a meaningful way. +* The micro:bit program uses Booleans in a way that is integral to the program. +* The program compiles and runs as intended and includes meaningful comments in code. +* Provide the written Reflection Diary entry. + ## Input -Remind the students of all the different inputs available to them through the micro:bit. -![micro:bit input list](/static/courses/csintro/variables/input-list.png) -  +Don't forget to consider all the different inputs available to you through the micro:bit. + +### Available inputs + +* Acceleration +* Light level +* Rotation +* Button is pressed +* Compass heading +* Temperature +* Running time +* On shake +* On button pressed +* On logo down +* On logo up +* On pin pressed +* On screen down +* On screen up +* Pin is pressed + ## Project Ideas +Use Boolean variables and/or random values to create: + +* A board game, game pieces, and holder for the micro:bit (or improve your board game from Unit 3: Variables) +* A mod of some sort to a current/existing board game +* A micro:bit version of a Magic Eight Ball + +## Project Examples + ### Sunscreen Monitor -When you shake the micro:bit, it reports the current temperature in degrees Fahrenheit. Button B measures the light level and if it is above 70 degrees AND very bright, it will display a sun icon. If it is above 70 degrees and less bright, it will display a cloudy symbol. If it is dark, it will display a nighttime icon. +The micro:bit is attached to a bottle of sunscreen and provides information about the temperature and if you need sunscreen: -[**micro:bit Sunscreen Monitor**](https://youtu.be/VmD-dcZZQFc) -https://youtu.be/VmD-dcZZQFc +* When you shake the micro:bit, it reports the current temperature in degrees Fahrenheit. +* Button A displays an animation to tell you whether or not you should use sunscreen (on sunny or cloudy days but not at night or indoors). +* Button B measures the light level, and if it is above 70 degrees AND very bright, it will display a sun icon. If it is above 70 degrees and less bright, it will display a cloudy symbol. If it is dark, it will display a nighttime icon. Check it out in action here: [youtu.be/VmD-dcZZQFc](here) (0:18) #### Sunscreen code @@ -170,33 +205,15 @@ input.onGesture(Gesture.Shake, () => { }) ``` -Button A displays an animation to tell you whether or not you should use sunscreen (on sunny or cloudy days but not at night or indoors.) - -Make a holder that can hold the micro:bit and a bottle of sunscreen. - -This example uses boolean operations because both light level AND temperature must be high in order to trigger the sun icon: - -```block -if (128 > input.lightLevel() && 0 < input.lightLevel() && input.temperature() > 22) {} -``` -### ~ hint - -The @boardname@ uses some clever tricks to measure both light and temperature. Want to see how it can measure the light level and temprature? Watch these videos to learn how it does it. - -https://www.youtube.com/watch?v=TKhCr-dQMBY -
-https://www.youtube.com/watch?v=_T4N8O9xsMA - -### ~ +Solution link: [Sunscreen Monitor](https://makecode.microbit.org/_Atd9Wti3MiUj) ### Two-player game -Create a game in which two players take turns on the same micro:bit. You can use a boolean variable called PlayerATurn to keep track of whose turn it is. +This is an example of a board game in which the micro:bit displays an arrow pointing in a random direction. The paper legend indicates different actions the player must take, and it uses a Boolean variable to keep track of whose turn it is. **Board Game:** Use boolean variables and random values as part of a board game (or improve your Board Game from the Variables lesson). Make the board and pieces and a holder for the micro:bit. Try modding a current board game. ![Two player game project](/static/courses/csintro/booleans/two-player-game.png) -Board Game with Arrows #### Board game arrow code @@ -305,10 +322,8 @@ spin = 0 player1Turn = true ``` - This is an example of a board game in which the micro:bit displays an arrow pointing in a random direction. The paper legend indicates different actions the player must take.  - Here is a portion of the board game's code. A boolean variable is used to determine whose turn it is. If player1Turn is false, then it's player 2's turn. A random number is generated to show the arrow seventy-five percent of the time (for values of 0, 1, or 2). ```blocks @@ -319,38 +334,15 @@ input.onGesture(Gesture.Shake, () => { } }) ``` + +Solution link: [Arrows Board Game](https://makecode.microbit.org/_1mY4wq4KPYiq) + ## Reflection -Have students write a reflection of about 150–300 words, addressing the following points: +Write a short reflection of about 150–300 words, addressing the following points: + * How did you incorporate boolean variables into your micro:bit program? * How did you incorporate boolean operators into your micro:bit program? * Describe something in your project that you are proud of. * If you had more time to work on this project, describe what you might add or change. - -## Assessment -  -**Competency scores**: 4, 3, 2, 1 -  -### Boolean - -**4 =** More than 2 Boolean variables are implemented in a meaningful way.
-**3 =** At least 2 Boolean variables are implemented in a meaningful way.
-**2 =** At least 1 Boolean variable is implemented in a meaningful way.
-**1 =** No Boolean variables are implemented. -   -### micro:bit program - -**4 =** micro:bit program:
-`*` Uses Booleans in a way that is integral to the program.
-`*` Compiles and runs as intended
-`*` Meaningful comments in code
-**3 =** micro:bit program lacks 1 of the required element.
-**2 =** micro:bit program lacks 2 of the required elements.
-**1 =** micro:bit program lacks all of the required elements. - -### Collaboration reflection - -**4 =** Reflection piece addresses all prompts.
-**3 =** Reflection piece lacks 1 of the required elements.
-**2 =** Reflection piece lacks 2 of the required elements.
-**1 =** Reflection piece lacks 3 of the required elements. +* Publish your MakeCode program and include the link. \ No newline at end of file diff --git a/docs/courses/csintro/booleans/unplugged.md b/docs/courses/csintro/booleans/unplugged.md index 2a7ba79135f..1af5a395f09 100644 --- a/docs/courses/csintro/booleans/unplugged.md +++ b/docs/courses/csintro/booleans/unplugged.md @@ -1,16 +1,15 @@ # Unplugged: Two heads are better than one -Materials: A penny for each student, paper and pencils +A penny, paper, and a pencil -Most students have used a penny to decide something. Ask for some examples. -Who goes first in a game, to break a tie, to decide which activity to do... +Most people have used a penny to decide something. Some examples might include: Who goes first in a game, to break a tie, to decide between two activities... A simple penny is the most common binary decision making tool ever! + When you flip a coin to decide something there are only two possible outcomes, heads or tails. -When you flip a coin the outcome is random. +When you flip a coin, the outcome is random. - -What’s a common issue with coin tosses? Students may bring up issues of trust and fairness. Who gets to flip the coin? Who gets to ‘call’ it? What if it’s a ‘faulty’ coin? +However, a common issue with coin tosses is that of trust and fairness. Who gets to flip the coin? Who gets to ‘call’ it? What if it’s a ‘faulty’ coin? Here’s a solution... The double coin toss. @@ -18,7 +17,7 @@ Here’s a solution... The double coin toss. In a double coin toss, both people have a coin and they flip the coins at the same time. -Working in pairs, have the students make a table or list of the possible outcomes if each student flipped a coin at the same time. +Make a table or list of the possible outcomes if each person flipped a coin at the same time. You should end up with something like this: Example: @@ -38,9 +37,9 @@ There are 4 possible outcomes. So, if 2 coins are flipped, the chance that the outcomes will be the same (HH/TT) is equal to the chance that the outcomes will be different (HT/TH). Both outcomes, coins the same/coins are different have a 2 in 4 or 50% chance of occurring. -Therefore, if Person A wins each time the outcomes are the same and Person B wins each time the outcomes are different, both have an equal chance of winning each double coin flip. With this system, no one person’s outcome, heads or tails, guarantees a win. If Person A’s coin flips to heads, she would win if Person B also flipped heads, but lose if Person B flipped tails. Students will usually see that this is a fair system. +Therefore, if Person A wins each time the outcomes are the same and Person B wins each time the outcomes are different, both have an equal chance of winning each double coin flip. With this system, no one person’s outcome, heads or tails, guarantees a win. If Person A’s coin flips to heads, she would win if Person B also flipped heads, but lose if Person B flipped tails. -Let the students experiment with this. Have students flip their coins together, keeping track of the outcomes, perhaps by adding another column to their table. +Experiment with this and record the results. Flip your coin twice, keeping track of the outcomes, perhaps by adding another column to their table. If there's another person around who can act as the second coin-flipper, that's all the better. Example: @@ -52,8 +51,6 @@ Example: Tails Heads Tails Tails ``` - -Just for fun, have them play to a certain total number of rounds. So, what does this have to do Boolean variables and operators? Think about how you would code a program a double coin flipper. How would you represent each of the 4 different possible double coin flip outcomes? @@ -66,7 +63,7 @@ We can create a Boolean variable to represent whether an outcome is heads or tai Note: Tails = False can also be thought of as Tails = not true. -Have the students copy their Heads/Tails table of possible outcomes, but label the columns "Coin A Heads" and "Coin B Heads" and replace each entry of ‘Heads’ with ‘True’ and ‘Tails’ with ‘False’. In the study of logic, this is known as a truth table. +Copy your Heads/Tails table of possible outcomes, but label the columns "Coin A Heads" and "Coin B Heads" and replace each entry of ‘Heads’ with ‘True’ and ‘Tails’ with ‘False’. In the study of logic, this is known as a **truth table.** We’ll use it to help us pseudocode our program, by adding a third column describing the results of each outcome. @@ -82,16 +79,14 @@ We’ll use it to help us pseudocode our program, by adding a third column descr Can we make this code more efficient? Can we combine any of these lines? Try using an OR to combine both conditions in which Player A scores a point. Do the same for both conditions in which Player B scores a point. -Give the students a chance to work this out on their own. - -Combining the conditions in which each player wins, gives us: +Combining the conditions in which each player wins gives us: * If (Coin A is true AND Coin B is true) OR (Coin A is false AND Coin B is false), add one to Player A score. * If (Coin A is true AND Coin B is false) OR (Coin A is false AND Coin B is true), add one to Player B score. -Note: Just as you do for math expressions with multiple operators, use parentheses to make it clear how the conditions and statements are grouped together. +>**Note:** Just as you do for math expressions with multiple operators, use parentheses to make it clear how the conditions and statements are grouped together. -The students are by now familiar with the MakeCode blocks. As they think through their algorithms, they may even have started to visualize the blocks they might use. Visualizing the blocks as they pseudocode can help them with the logical steps of their program. It can also help them to visualize and recognize the big picture of their code as well as the details. +By now, you should be familiar with the MakeCode blocks. As you think through your algorithms, you may even have started to visualize the blocks you might use. Visualizing the blocks as you pseudocode can help with the logical steps of your program. It can also help you to visualize and recognize the big picture of your code as well as the details. Using blocks to start coding these two conditionals as currently written, might look like this: @@ -115,7 +110,7 @@ Though this code will work as we want it to, it’s a lot of code. It is good pr ## Booleans and simplifying code -A boolean can have only one of two values: True or False. Conditionals like 'if...then' check whether a condition is true. Notice that the default condition for the 'if...then' blocks is true. In other words, the 'if...then' blocks will check to see whether whatever condition you place there is true. +A boolean can have only one of two values: **True** or **False.** Conditionals like **'if...then'** check whether a condition is true. Notice that the default condition for the **'if...then'** blocks is true. In other words, the **'if...then'** blocks will check to see whether whatever condition you place there is true. ```block basic.forever(() => { if (true) { } }) @@ -173,7 +168,7 @@ basic.forever(() => { We use a coin flip to decide things because the result is random, meaning the result happens without any conscious decision or direction. We use dice and game spinners for the same reason. The results are not predetermined or directed in any way. -So, how do we get a random flip in code? Most computer programming languages have a built in function that will select a random number given a range of values. Microsoft MakeCode has a block for this. And it also has a block for getting a random true or false value. +So, how do we get a random flip in code? Most computer programming languages have a built in function that will select a random number given a range of values. Microsoft MakeCode has a block for this. And it also has a block for getting a random true or false value. We will call on this built in function to get a random true or false value for each flip of a coin in the next [activity](/courses/csintro/booleans/activity). @@ -184,4 +179,4 @@ Our basic pseudocode for our 'double coin flipper' could look like this: 3. Compare the current values of Coin A and Coin B. 4. If the current true/false values of Coin A and Coin B are the same, add a point to Player A’s score. 5. Otherwise, the current true/false values of Coin A and Coin B must be different, so add a point to Player B’s score. -6. When players are done with their double coin flipping, show the final scores for each player. +6. When players are done with their double coin flipping, show the final scores for each player. \ No newline at end of file diff --git a/docs/courses/csintro/conditionals.md b/docs/courses/csintro/conditionals.md index 85bf614518d..9780e0e9b07 100644 --- a/docs/courses/csintro/conditionals.md +++ b/docs/courses/csintro/conditionals.md @@ -2,27 +2,21 @@ ![Board game example](/static/courses/csintro/conditionals/cover.jpg) -This lesson introduces the Logic blocks such as 'If...then' and 'If...then...else'. -Students practice skills of creativity, problem-solving, and collaboration. +This unit introduces the Logic blocks, such as ‘Ifâ€Ļthen’ and ‘Ifâ€Ļthenâ€Ļelse’. You will learn what **conditional** statements are, and why and when to use them in a program, practicing skills of creativity, problem solving, and collaboration in the process. You will code a game of “Rock, paper, scissors” with the micro:bit via the programmable buttons and the LED screen. In the final project, you'll be designing, building, and coding your own unique micro:bit-based board game using conditionals. ## Lesson objectives -Students will... +You will... * Understand what conditional statements are, and why and when to use them in a program. * Learn how to use the Logic blocks 'If...then' and 'Ifâ€Ļthen...else'. * Practice using the Logic blocks so different conditions yield specified outcomes. -* Demonstrate understanding and apply skill by collaborating with classmates to create a game that uses a micro:bit and a program that correctly and effectively uses conditionals. +* Demonstrate understanding and apply skill by creating a game that uses a micro:bit and a program that correctly and effectively uses conditionals. ## Lesson plan 1. [**Overview**: Conditional statements](/courses/csintro/conditionals/overview) -2. [**Unplugged**: Red light, green light](/courses/csintro/conditionals/unplugged) -3. [**Activity**: Rock, paper, scissors](/courses/csintro/conditionals/activity) -4. [**Project**: Board game](/courses/csintro/conditionals/project) - -## Flipgrid - -The [Flipgrid](https://info.flipgrid.com/) topic for the **Conditionals** lesson: https://flipgrid.com/f260eda7 +2. [**Activity**: Rock, paper, scissors](/courses/csintro/conditionals/activity) +3. [**Project**: Board game](/courses/csintro/conditionals/project) ## Related standards diff --git a/docs/courses/csintro/conditionals/activity.md b/docs/courses/csintro/conditionals/activity.md index 35be976fe31..2cc6bbf14db 100644 --- a/docs/courses/csintro/conditionals/activity.md +++ b/docs/courses/csintro/conditionals/activity.md @@ -1,28 +1,29 @@ # Activity: Rock, paper, scissors -For this activity, each student will need a micro:bit. -Everyone will create the same program, the classic rock paper scissor game. +In this micro:bit activity, you will create a *Rock, Paper, Scissor* game program with conditionals. In *Unit 3: Variables*, you coded your micro:bit to keep score, and in this unit you will code to play *Rock, Paper, Scissors* with the micro:bit. ![Rock, paper, scissors](/static/courses/csintro/conditionals/rock-paper-scissors-items.png) -## Introduce activity +## Introduction -* Have students recall the classic rock paper scissors game. -* What are the rules of the game? What are the conditionals? ->Example: If Player A gets rock, and Player B gets scissors, Then Player A wins. -* Have students write the pseudocode for how to play the game on the micro:bit. ->Example pseudocode:
-On button A press: choose random number from 0-2 -If random number = 0, then display rock icon, -Else if random number = 1, then display paper icon, -Else display scissors icon. -* Point out that because there are only three possibilities, we don’t need to do a separate check to see if random number = 2. So we just use an else. +Let's come up with some pseudocode to describe the behavior of the classic *Rock, Paper, Scissors* game. Your code might look something like this: + +>Example pseudocode: + +>On shake: choose random number from 0-2 +>IF random number = 0, THEN display rock icon, +>ELSE if random number = 1, THEN display paper icon, +>ELSE display scissors icon. + +Because there are only three possibilities, we don’t need to do a separate check to see if random number = 2. So, we just use ELSE. ## micro:bit -* Working from the specifications, have students work in pairs to try to code a Rock Paper Scissors game on their own. -* If students get stuck, there is a tutorial at [rock, paper, scissors](/projects/rock-paper-scissors) (steps 1 through 4), that leads students step-by-step through the process of coding a working rock paper scissor game for their micro:bit. -* Let them play the game against their program. +You should now have the information you need in order to begin coding a Rock, Paper, Scissors game on your own. If you get stuck, or if you would prefer to work along with a tutorial, you can find one here: [rock, paper, scissors](/projects/rock-paper-scissors) (steps 1 through 4) + +Once you've finished, play a few games against your program! + +The solution code can be found here: [Rock Paper Scissors](https://makecode.microbit.org/_D2DCDoJbEYat) ## Ideas for Mods diff --git a/docs/courses/csintro/conditionals/overview.md b/docs/courses/csintro/conditionals/overview.md index 9c0f77baa4e..63c8b2c2bee 100644 --- a/docs/courses/csintro/conditionals/overview.md +++ b/docs/courses/csintro/conditionals/overview.md @@ -1,31 +1,27 @@ # Introduction -Computer programs are instructions telling the computer how to process input and deliver output. -An important part of programming is telling the computer WHEN to perform a certain task. -For this, we use something called ‘conditionals’. Conditionals get their name because a certain Condition or Rule has to be met. +Computer programs are instructions telling the computer how to process input and deliver output. An important part of programming is telling the computer WHEN to perform a certain task. For this, we use something called **conditionals**. Conditionals get their name because a certain condition or rule must be met in order for an action to be carried out. -Students are all already familiar with the concept of conditionals in their daily lives! +Fortunately, people use conditionals all the time in their daily lives! -Have they ever had their parents say..? -* “If you clean your room, you can go out with your friends.” -* “If your homework is done, you can play video games.” -* “If you do your chores all week, you get your allowance, else you are grounded.” +Have you ever heard your parents say: + +* "If you clean your room, you can go out with your friends." +* "If your homework is done, you can play video games." +* "If you do your chores all week, you get your allowance, else you are grounded." These are all conditionals! Conditionals follow the format of IF this, THEN that. ->**IF** (condition is met), **THEN** (action performed) -Have the students share a few conditionals from their own lives with the class or within small groups. +**IF** (condition is met), **THEN** (action performed) -Note: For older students, you can have them add the ELSE portion of a conditional. ->**IF** (condition is met), **THEN** (action performed), **ELSE** (different action performed) +Try to think of a few conditionals from your own life. Once you've gotten the hang of it, add the ELSE portion of a conditional. -Example: -* IF it is snowing, THEN wear boots, ELSE wear shoes. +**IF** (condition is met), **THEN** (action performed), **ELSE** (different action performed) -The ELSE portion makes sure that a different action is performed in either case. Without the ELSE action, your students might be barefoot! +Example: -![If-Then workflow](/static/courses/csintro/conditionals/flowchart.PNG) +* **IF** it is snowing, **THEN** wear boots, **ELSE** wear shoes. -Tell the students that they will be acting out some conditionals as though the whole class is a computer program for a game. Each student will perform a described action if the indicated condition is met. +The ELSE portion makes sure that a different action is performed in either case. Without the ELSE action, you might end up barefoot! -**Note:** This activity can be done as a whole class or in smaller groups or as a pencil and paper activity. +![If-Then workflow](/static/courses/csintro/conditionals/flowchart.PNG) \ No newline at end of file diff --git a/docs/courses/csintro/conditionals/project.md b/docs/courses/csintro/conditionals/project.md index 45ce83d8936..f71870bbc20 100644 --- a/docs/courses/csintro/conditionals/project.md +++ b/docs/courses/csintro/conditionals/project.md @@ -2,47 +2,47 @@ ![Close-up of game tokens](/static/courses/csintro/conditionals/game-pieces.jpg) -This is an assignment for students to create a board game. It should take two to three class periods. If your school has a makerspace or an art classroom where students can access materials such as cardboard, poster paints, or markers, you might schedule your classes to work there. - -Once students have finished the first version of their games, schedule time for students to play each other’s games. Ideally, give them some time to give and gather feedback, then revise their games accordingly. - ## Introduction + Many board games use an electronic toy to signal moves, or provide clues. There are some funny examples online if you search for “electronic board game”. Here are some examples: [Dark Tower](https://youtu.be/cxrY7MWEkwE) (featuring Orson Welles): This is an example of a circular board game in which the pieces start on the edges and move in toward the middle. -[Electronic Dream Phone Board Game Commercial - 1992](https://www.youtube.com/watch?v=pqYsQgDqlmg): This board game is really a logic puzzle. There are printed clues that illustrate relationships and the phone provides clues that help you to narrow down possibilities by a process of elimination. - [Stop Thief Electronic Board Game commercial 1979](https://www.youtube.com/watch?v=q3wpPRdDy4E): This board game uses a device to give audio clues that help you to figure out what to do on the game board. It’s a good example of how you might use sound as a clue. ## Assignment -Students should work in pairs to create an original board game project in which micro:bit is a central feature, and the rules of their board game should use Conditionals. -Students will need to work together to come up with: +Create an original board game project in which micro:bit is a central feature. The rules of your board game should use Conditionals. + +Come up with: + * A set of written rules (how to play) * A game board * A program for the micro:bit -* Photo documentation of the different game pieces, cards, or other components of the game with the micro:bit included as well as a screenshot of your micro:bit code. Each photo must have a caption that describes what the photo is documenting. -* Reflection: A text entry describing your team’s game making process and each teammate’s part in the creation of the game from brainstorming ideas, through construction, programming, and beta testing. +* Photo documentation of the different game pieces, cards, or other components of the game with the micro:bit included as well as a screenshot of your micro:bit code. Each photo should have a caption that describes what the photo is documenting. +* Reflection: A text entry describing your game-making process from brainstorming ideas, through construction, programming, and beta testing. The micro:bit needs to work in conjunction with the game board and/or game pieces and should be a central feature of the game. Ideally, it should be more than a simple substitute for a six-sided die. The micro:bit might: + * Simulate the results of a battle between two pieces * Randomly point in a different direction of travel * Generate a result based on its current incline -* Point randomly at players and kill them +* Point randomly at players and eliminate them * Display a dynamic score -* ... let your imaginations run wild! +* ...let your imagination run wild! -Ideally, students should be writing their own versions of micro:bit programs to do something original. -Here is one simple program to discuss and use as an example: +Try to code your micro:bit to do something original. Here is one example: ![Close-up of game tokens](/static/courses/csintro/conditionals/battle-pieces.jpg) ### Battle pieces -In this example, pieces start out at full strength and lose points based on random events on the board. When two pieces meet on the same space, they battle. +In this example, pieces start out at full strength and lose points based on random events on the board. + +Rules: When two pieces meet on the same space, they battle. + * Press A to enter the strength of piece A. * Then press B to enter the strength of piece B. * Shake the micro:bit to determine the winner of the battle, which is proportionately random to the strength of each piece. @@ -59,7 +59,7 @@ input.onButtonPressed(Button.B, () => { basic.showNumber(p2) }) input.onGesture(Gesture.Shake, () => { - if (randint(0, p1 + p2 - 1 + 1) + 1 <= p1) { + if (randint(0, p1 + p2) <= p1) { basic.showString("A") } else { basic.showString("B") @@ -67,59 +67,38 @@ input.onGesture(Gesture.Shake, () => { }) ``` +Solution link: [Battle Pieces Project](https://makecode.microbit.org/_0fx9hY9EbM5T) + ### ~ hint -The @boardname@ uses its accelerometer to detect when you're shaking it. How does an accelerometer actually work? +#### Bonus + +The micro:bit uses its accelerometer to detect when you're shaking it. How does an accelerometer actually work? https://www.youtube.com/watch?v=byngcwjO51U ### ~ -## Beta Testing - -Give students a chance to play each other’s games. The following process works well: -* Have each pair of students set up their own project at their table. -* Leave a clipboard or a laptop on the table for taking notes. -* Rotate the students through each project, moving clockwise around the room: ->* Play the game (5 min) ->* Fill out a survey form (5 min) +### Space Race -Sample Survey questions -* How easy was it to figure out what to do? -* What is something about this project that works really well? -* What is something that would make this project even better? -* Any other comments or suggestions? +How to win: Starting from Earth, your goal is to progress to Mars. The first person to reach Mars is the winner. -Many online survey tools will allow you to sort the comments by project and share them with project creators so they can make improvements based on that feedback. +Rules: -## Reflection - -Have students write a reflection of about 150–300 words, addressing the following points: -* Explain how you decided, as a pair, on your particular board game idea. -* What was something that was surprising to you about the process of creating this game? -* Describe a difficult point in the process of designing this game, and explain how you resolved it. -* What feedback did your beta testers give you? How did that help you improve your game? What were the Conditionals that you used as part of your game rules? - -## Board game example - -Space Race by K. and S. -* How to win: Starting from Earth, your goal is to progress to Mars. The first person to reach Mars is the winner. -* Rules:
->**1** - Shake the micro:bit to randomize how far you get to advance.
-**2** - If you land on a pink square, press “B” on the micro:bit until your previous roll number appears. Then press A and B at the same time to see whether or not you move based upon the number on the square.
-**3** - Up to four players. +* Shake the micro:bit to randomize how far you get to advance. +* If you land on a pink square, press B on the micro:bit until your previous roll number appears. Then press A and B at the same time to see whether or not you move based upon the number on the square. +* Up to four players. ![Space race game](/static/courses/csintro/conditionals/space-race.jpg) -Finished game +_Finished game_ ![micro:bit holder square](/static/courses/csintro/conditionals/microbit-holder.jpg) -micro:bit holder +_micro:bit holder_ ![Game pieces](/static/courses/csintro/conditionals/game-pieces.jpg) -Game pieces +_Game pieces_ ```blocks - let yes_or_no = 0 let current_roll = 0 let previous_roll = 0 @@ -157,54 +136,14 @@ basic.showString("SPACE RACE") previous_roll = 0 ``` -## Assessment - -**Competency scores**: 4, 3, 2, 1 - -### Rules - ->**4 =** All game rules are clear and complete.
-**3 =** A game rule is missing or not complete or not clear.
-**2 =** More than one game rule is missing or not complete or not clear
-**1 =** Most of the game rules are missing or it is not clear what the rules are. - -### Game board +Solution link: [Space Race Project](https://makecode.microbit.org/_H7kPewAyifhk) ->**4 =** Game board is:
-`*` Complete
-`*` Neat
-`*` Fits with the theme of the game
-`*` micro:bit is a central part of the game
-**3 =** Game board meets only 3 of the conditions listed for a score of 4.
-**2 =** Game board meets only 2 of the conditions listed for a score of 4.
-**1 =** Game board meets only 1 of the conditions listed for a score of 4. - -### micro:bit program - ->**4 =** micro:bit program:
-`*` Uses the micro:bit in a way that is integral to the game
-`*` Uses conditionals correctly
-`*` Compiles and runs as intended
-`*` JavaScript includes comments in code
-**3 =** micro:bit program lacks 1 of the required elements.
-**2 =** micro:bit program lacks 2 of the required elements.
-**1 =** micro:bit program lacks 3 of the required elements. - -### Photo documentation - ->**4 =** Complete photo documentation that includes photos of game board and code and captions.
-**3 =** A photo is missing or of poor quality or a caption is missing.
-**2 =** Multiple photos and/or captions missing or of poor quality.
-**1 =** Most photos and/or captions missing or of poor quality. - -### Collaboration reflection ->**4 =** Reflection piece includes:
-`*` Brainstorming ideas
-`*` Construction
-`*` Programming
-`*` Beta testing
-**3 =** Reflection piece lacks 1 of the required elements.
-**2 =** Reflection piece lacks 2 of the required elements.
-**1 =** Reflection piece lacks 3 of the required elements. +## Journal Prompt +Write a short reflection in your journal (about 150–300 words), addressing the following points: +* Explain how you decided on your particular board game idea. +* What was something that was surprising to you about the process of creating this game? +* Describe a difficult point in the process of designing this game, and explain how you resolved it. +* If you had other people play your game, what feedback did they give you? How did that help you improve your game? What were the Conditionals that you used as part of your game rules? +* Publish your MakeCode program and include the link. diff --git a/docs/courses/csintro/conditionals/unplugged.md b/docs/courses/csintro/conditionals/unplugged.md index 84255c62875..f6ff0b7b8d1 100644 --- a/docs/courses/csintro/conditionals/unplugged.md +++ b/docs/courses/csintro/conditionals/unplugged.md @@ -3,14 +3,17 @@ ![Red and green stoplight](/static/courses/csintro/conditionals/traffic-light.png) ## Objective + To reinforce the programming of basic conditionals by having students experience conditionals through acting them out in real life. ## Activity overview + Students will line up at one end of the classroom with the goal of reaching the other side of the classroom. The teacher, and then the students themselves will call out conditionals and all the students will advance or not depending on the specific conditional statement. **Note:** As the teacher you will need to keep an eye out for any ‘errors’ that occur during the running of the program. ## Materials + * Pencils and lined paper (if doing this activity seated). Students can advance across the paper instead of the room with one inch line equal to one step. ## Process @@ -34,13 +37,16 @@ After the students get the idea of the game, allow them to make up and call out They will need to be observant, as a conditional that moves them forward, will also move their competition forward! ## Tips + * SAFETY FIRST! Students, especially younger ones, can get quite silly with this and while it is meant to be fun and even funny, safety first! * Student conditionals need to apply to at least two people in the class. ## Reflections + How did they do? Were there any ‘run-time errors’? Did a student miss a conditional being met or fail to correctly carry out the THEN or ELSE action?  Were there some conditions that could be evaluated as something other than True or False (maybe, sometimes)? ## Extensions/Variations + * Add AND, OR, AND/OR statements to the conditionals. >Example: If you have brown hair AND brown eyes, then... * Create nested IF’s diff --git a/docs/courses/csintro/coordinates.md b/docs/courses/csintro/coordinates.md index e778fa082d7..139d619eb11 100644 --- a/docs/courses/csintro/coordinates.md +++ b/docs/courses/csintro/coordinates.md @@ -2,14 +2,15 @@ ![Sample Heart Simulator](/static/courses/csintro/coordinates/cover.png) -This lesson introduces the use of coordinates to store data or the results of mathematical operations. It gives students practice programming for the LEDs of the micro:bit screen using coordinates, and introduces the basic game blocks of MakeCode. +This lesson introduces the use of coordinates to store data or the results of mathematical operations. It provides practice programming for the LEDs of the micro:bit screen using coordinates, and introduces the basic game blocks of MakeCode. ## Lesson objectives -Students will... +You will... + * Understand that the 5 x 5 grid of LEDs on the micro:bit represents a coordinate grid with the origin (0,0) in the top left corner. -* Understand that the values of the x coordinates range from 0 through four and increase from left to right. -* Understand that the values of the y coordinates range from 0 through four and increase from top to bottom. +* Understand that the values of the x-coordinates range from 0 through 4 and increase from left to right. +* Understand that the values of the y-coordinates range from 0 through 4 and increase from top to bottom. * Learn how to refer to an individual LED by its **X** and **Y** coordinates. * Learn how to plot (turn on) and unplot (turn off) individual LEDs and how to toggle between these two states. * Learn how to check the current on or off status of an individual LED as well as check and set the brightness level. @@ -18,22 +19,15 @@ Students will... ## Lesson structure * Introduction: Coordinate Grid -* Unplugged Activity: Battleship * micro:bit Activities: Animation and Patterns * Project: Screensaver or Game -* Assessment: Rubric * Standards: Listed ## Lesson plan 1. [**Overview**: Coordinate grid and LEDs](/courses/csintro/coordinates/overview) -2. [**Unplugged**: Battleship](/courses/csintro/coordinates/unplugged) -3. [**Activity**: Animation and patterns](/courses/csintro/coordinates/activity) -4. [**Project**: Screensaver or game](/courses/csintro/coordinates/project) - -## Flipgrid - -The [Flipgrid](https://info.flipgrid.com/) topic for the **Coordinates** lesson: https://flipgrid.com/699ca0b7 +2. [**Activity**: Animation and patterns](/courses/csintro/coordinates/activity) +3. [**Project**: Screensaver or game](/courses/csintro/coordinates/project) ## Related standards diff --git a/docs/courses/csintro/coordinates/activity.md b/docs/courses/csintro/coordinates/activity.md index 94f98392f1d..92957758387 100644 --- a/docs/courses/csintro/coordinates/activity.md +++ b/docs/courses/csintro/coordinates/activity.md @@ -1,18 +1,21 @@ # Activity: Animation and patterns -Guide the students to create programs using coordinates and LEDs. Each of these short exercises demonstrates how to use coordinates to control the LEDs. These programs can then be modified and used in the students’ more complex projects. +Each of these short exercises demonstrates how to use coordinates to control the LEDs. These programs can later be modified and used in your more complex projects.   * Smile animation - A short exercise in plotting and toggling LEDs to create a simple animation. * Random Patterns generator - A short exercise using a loop to generate random LED patterns and then checking the status of a specific LED. * Brightness - A short exercise in using the brightness settings for the micro:bit LEDs. -## Smile animation +## Coding activity 1: Smile animation -A short exercise in plotting and toggling LEDs to create a simple animation. -* Though students can use the 'show leds' block for images and animation, there is another way to tell the micro:bit what LEDs to turn on and off using coordinates. -* We can still use the 'show leds' block to plan which LED coordinates to turn on -* Drag out a couple 'show leds' blocks from the Basic Toolbox drawer. -* Create a smiling face and a non-smiling face. +A short exercise in plotting and toggling LEDs to create a simple animation. Although you can use the 'show leds' block for images and animation, there is another way to tell the micro:bit what LEDs to turn on and off using coordinates. + +### Create a smiling and non-smiling face with 'show leds' blocks + +We can still use the 'show leds' block to plan which LED coordinates to turn on. + +* In Microsoft MakeCode, start a new project and name it something like **'smile animation'.** +* From the Basic Toolbox drawer, drag out a couple of 'show leds' blocks from the Basic Toolbox drawer to the coding Workspace. Since we'll be using them as a guide, **don't** connect them to the 'on start' block. Remember, the blocks will be grayed out, but you can still create a smiling face in one and non-smiling face in the other by selecting the squares you want. ```block basic.showLeds(` @@ -31,11 +34,13 @@ basic.showLeds(` `) ``` -* From the LED Toolbox drawer, drag out 6 'plot x y' blocks. +### Plot coordinates for LEDs that are in both images + +* From the LED Toolbox drawer, drag out 6 **'plot x y'** blocks and connect them inside on the **'on start'** block. >* Tip: you can also right-click on a block and select Duplicate to copy blocks -* Have the students compare the two face images and determine which LEDs are on in both images. +* Compare the two face images and determine which LEDs are on in both images. * Plot these LEDs using the correct (x,y) coordinates. * When done, place these 'plot x y' blocks inside an 'on start' block. @@ -47,15 +52,18 @@ led.plot(1, 3) led.plot(2, 3) led.plot(3, 3) ``` +### Code the toggle coordinates Now we can code for the 4 LEDs that change back and forth, on and off, as we switch from one face to the other and back again over and over. -* From the LED Toolbox drawer, drag out 4 'toggle x y' blocks. -* Replace the default values with the correct (x,y) coordinates. -The 'toggle x y' block will change the status of an LED from on to off or off to on. -* Place these 4 'toggle x y' blocks in a 'forever' block. -* Place the two 'toggle x y' blocks that create the smile first, followed by the two 'toggle x y' blocks for the non-smile. -* You may notice that the toggling happens too quickly. Let’s slow it down a bit by placing a 'pause' block between the two pairs of 'toggle x y' blocks. Set the pause value to 250 milliseconds. +* From the LED Toolbox drawer, drag out 4 **'toggle x y'** blocks and connect them to the **'forever'** block. The **'toggle x y'** block will change the status of an LED from on to off and off to on. +* Determine the coordinates for the LEDs that will toggle. +* First, update the default (0,0) coordinates with the correct (x,y) coordinates for the two LEDs to create the smile in the first two 'toggle x y' blocks. Then, update the other two 'toggle x y' blocks with the correct (x,y) coordinates for the two LEDs to create the non-smile. +* Run the code in the Simulator to check it out. It toggles all four blocks at the same time! Let's fix it. + +### Add a pause between the smile and non-smile + +* You may notice that the toggling happens too quickly. Let's slow it down a bit by placing a 'pause' block between the two pairs of 'toggle x y' blocks. Set the pause value to 250 milliseconds. Here is the full program: @@ -74,9 +82,12 @@ led.plot(1, 3) led.plot(2, 3) led.plot(3, 3) ``` +Solution link: [Smile Animation](https://makecode.microbit.org/_AjmLfgc31EMk) ### ~ hint +#### Amazing LEDs + LEDs are amazing little devices. If you haven't seen this video about how they work, take a few minutes to learn more about them. https://www.youtube.com/watch?v=qqBmvHD5bCw @@ -86,121 +97,103 @@ https://www.youtube.com/watch?v=qqBmvHD5bCw ## Mod this! * Add a third image to the animation, perhaps a frown face. * Make your own custom animation! What LEDs stay the same and which need to be toggled? -  -## Random patterns generator + +## Coding activity 2: Random patterns generator A short exercise using a loop to generate random LED patterns and then checking the status of a specific LED. + Pseudocode: -* On button A pressed we’ll use a loop to turn on a random set of LED lights on our micro:bit. + +* On button A pressed we'll use a loop to turn on a random set of LED lights on our micro:bit. * Our display will have one LED lit for each column or x coordinate value from 0 through 4. -Steps: -* From the Input Toolbox drawer, select the 'on button pressed' block -* From the Basic - More Toolbox drawer, drop in a 'clear screen' block -* From the Loops Toolbox drawer, drop in a 'for' block -* From the LED Toolbox drawer, drop a 'plot x y' block -* Use the variable 'index' for the x value -* From the Math Toolbox drawer, drop a 'pick random' block into the y value -```blocks -input.onButtonPressed(Button.A, () => { -   basic.clearScreen() -   for (let index = 0; index <= 4; index++) { -       led.plot(index, randint(0, 5)) -   } -}) -``` -  -Check the on/off state of an LED -* On button B pressed we’ll use an 'if...then...else' block from the Logic Toolbox drawer -* From the LED Toolbox drawer, drop a 'point x y' block into the 'if' condition to check the current on/off state of a specific LED. +### Code button A ->* If the LED is currently on, the point x y block will return true. -* If the LED is currently off, the point x y block will return false. +* Start a new MakeCode project and name it. We don't need the 'on start' or 'forever' blocks in this activity, so delete them from the coding Workspace or move them to the side. +* From the Input Toolbox drawer, select the 'on button pressed' block. +* From the Basic – More Toolbox drawer, drop a 'clear screen' block in the 'on button pressed' block. +* From the Loops Toolbox drawer, drop in a 'for' block under the 'clear screen' block. +* From the LED Toolbox drawer, drop a 'plot x y' block into the 'for' block. +* From the Variable Toolbox drawer, drag an 'index' variable into the x value in the 'plot x y' block. +* From the Math Toolbox drawer, drag a 'pick random' block into the y value and change the second parameter from 0 to 4. -* For this exercise, we’ll use the two Yes/No built in icons to display the LED’s current status. From the Basic Toolbox drawer, drag 2 'show icon' blocks into each of the 'then' and 'else' clauses. Select the check mark for Yes, and the X icon for No. -* For now, we’ll leave the default coordinate values (0,0). But you can challenge your students to add a loop to test for all coordinates on the micro:bit. +### Code button B -Here is the complete program: +Now, we'll code to check the on/off state of one of the LEDs with button B. -```blocks -input.onButtonPressed(Button.A, () => { -   basic.clearScreen() -   for (let index = 0; index <= 4; index++) { -       led.plot(index, randint(0, 5)) -   } -}) -input.onButtonPressed(Button.B, () => { -   if (led.point(0, 0)) { -       basic.showIcon(IconNames.Yes) -   } else { -       basic.showIcon(IconNames.No) -   } -}) -``` +* From the Input Toolbox drawer, drag an 'on button pressed' block to the coding Workspace and use the dropdown menu to select B. +* On button B pressed, we'll use an 'If then else' block from the Logic Toolbox drawer. +* From the LED Toolbox drawer, drop a 'point x y' block into the 'if' condition to check the current on/off state of a specific LED. This means: + * If the LED located at (0,0) on the micro:bit is currently on, the 'point x y' block will return true. + * If the LED located at (0,0) is currently off, the 'point x y' block will return false. +* For this exercise, we'll use the two built-in icons to display the LED's current status. From the Basic Toolbox drawer, drag two 'show icon' blocks into each of the 'then' and 'else' clauses +* Use the dropdown menu to select the check mark for Yes, and the X icon for No. + +Solution link: [Random Pattern Generator](https://makecode.microbit.org/_hT458oiDR7AL) ### Try it out! + * Download the program to your micro:bit * Press button A to create a random pattern * Press button B to check and display the status of the specific LED -  -## Brightness -A short exercise in using the brightness settings for the micro:bit LEDs. Important to note - the brightness level of the micro:bit simulator LEDs will NOT appear to change! You must run your program on the actual micro:bit to see the different brightness levels. -We will check on, and numerically display the brightness level with our program, so we can verify with the simulator that it is working. +### Mod this! -Pseudocode: -We’ll set the brightness level for the LEDs to the highest level on start and then use on button A pressed to decrease the brightness level and on button B pressed to increase the brightness level. We’ll use on button A+B pressed to check and display numerically the current brightness level. +Add a loop to test for all coordinates on the micro:bit when button B is pressed instead of testing only for the (0, 0) LED. -Steps: -* Drag 3 'set brightness' blocks and 3 'brightness' blocks from the Led - More Toolbox drawer onto your coding workspace -* Place one 'set brightness' block in the 'on start' block -* Add a 'show icon' block after the 'set brightness' block so we will have an image to look at +## Coding activity 3: Brightness -```blocks -led.setBrightness(255) -basic.showIcon(IconNames.Heart) -``` -  -* From the Input Toolbox drawer, drag out 3 'on button pressed' blocks onto your coding workspace -* Leave one 'on button pressed' block with the default setting of A and change the second one to B and the third one to A+B -* Place one 'set brightness' block in the 'on button A' pressed block, and the other 'set brightness' block in the 'on button B' pressed block -* From the Math Toolbox drawer, drag out an addition block and a subtraction block -* Place the addition block within the 'set brightness' block in the 'on button B pressed' block -* Place the subtraction block within the 'set brightness' block in the 'on button A pressed' block -* Place a 'brightness' block on the left side of each math expression -* Change the default value of 0 on the right side of each math expression to 25 +A short exercise in using the brightness settings for the micro:bit LEDs. Our program will change the brightness of the LEDs and numerically display the brightness level. -```blocks -input.onButtonPressed(Button.A, () => { -   led.setBrightness(led.brightness() - 25) -}) -input.onButtonPressed(Button.B, () => { -   led.setBrightness(led.brightness() + 25) -}) -``` -  -Since we cannot see if our program is working in the simulator, let’s add a check into our code. +### Pseudocode -* On button A+B pressed, we’ll clear the screen and get and display the current brightness level as a number. -* Then we’ll re-display the image we used on start. +* We'll set the brightness level for the LEDs to the highest level on start and then use on button A pressed to decrease the brightness level, and on button B pressed to increase the brightness level. +* We'll use on button A+B pressed to check and display numerically the current brightness level. -Here is the complete program: +### Code for the start -```blocks -input.onButtonPressed(Button.A, () => { -   led.setBrightness(led.brightness() - 25) -}) -input.onButtonPressed(Button.B, () => { -   led.setBrightness(led.brightness() + 25) -}) -input.onButtonPressed(Button.AB, () => { -   basic.clearScreen() -   basic.showNumber(led.brightness()) -   basic.showIcon(IconNames.Heart) -}) -led.setBrightness(255) -basic.showIcon(IconNames.Heart) -``` +* Start a new project in MakeCode and name it. +* Select the LED Toolbox drawer. You'll notice a **“â€Ļmore”** Toolbox drawer appear; select that and drag a **'set brightness'** block onto the coding Workspace. Drop this into the **'on start'** block. +* From the Basic Toolbox drawer, drag a **'show icon'** block and drop it after the **'set brightness'** block so we will have an image to look at. + +### Code for button A and button B + +* From the Input Toolbox drawer, drag out three **'on button pressed'** blocks onto your coding Workspace. +* Leave one **'on button pressed'** block with the default setting of A and use the dropdown menu to change the other to B, and the third to A+B (this is when buttons A and B are pressed together) +* From the LED â€Ļmore Toolbox drawer, drag out two more **'set brightness'** blocks and drop one each into the **'on button A pressed'** and **'on button B pressed'** blocks. +* From the Math Toolbox drawer, drag out an **addition** block and a **subtraction** block. +* Drop the addition block within the **'set brightness' **block in the **'on button B pressed'** block, and the subtraction block within the **'set brightness'** block in the **'on button A pressed'** block, replacing the default value of '255'. +* From the LED â€Ļmore Toolbox drawer, drag out two **'brightness'** value blocks to the Workspace and drop one into the **first slot of the subtraction block,** and drop the second one into the **first slot of the addition block.** +* Change the default value of 0 on the right side of each math expression to 25. + +### Code for button A + B together + +Since we can't see if our program is working in the simulator, let's add a check into our code. + +* From the Basic â€Ļmore Toolbox drawer, drag a **'clear screen'** block onto the Workspace and drop it into the **'on button A+B pressed'** block. +* From the Basic Toolbox drawer, drag a **'show number'** block onto the Workspace and drop it below the **'clear screen'** block. +* From the LED â€Ļmore Toolbox drawer, drag another **'brightness'** value block onto the Workspace and drop it into the **'show number'** block, replacing the default 0 value. This will clear the screen when buttons A and B are pressed and display the current brightness level as a number on the micro:bit screen. +* Now, from the Basic Toolbox drawer, drag a **'show icon'** block to the coding Workspace and connect it below the **'show number'** block. This will re-display the image we used on start of the program + +Solution link: [Show Brightness](https://makecode.microbit.org/_JjwMLL6Da3jP) ### Try it out! -What happens if adding 25 or subtracting 25 from the current brightness level would result in a sum or difference outside of the 0 to 255 brightness range? +Check it in the simulator first, then download the program to the micro:bit to run the program. + +### Mod this! + +What happens if adding 25, or subtracting 25, from the current brightness level would result in a sum or difference outside of the 0 to 255 brightness range? + +## Knowledge Check + +**Questions:** + +1. How many coordinate pairs are represented on the micro:bit LED screen? +2. We've learned how to light LEDs on the micro:bit screen using blocks from three different Toolbox drawers. What are the three Toolbox drawers? +3. What type of variable is the (x,y) coordinate? + +**Answers:** + +1. 25 +2. **Basic** (i.e., 'show leds' and 'show icon' blocks); **Led** (i.e., 'plot', 'unplot', 'toggle', and 'point'); and **Game** under Advanced (i.e., 'sprite move by', 'sprite turn') +3. sprite \ No newline at end of file diff --git a/docs/courses/csintro/coordinates/overview.md b/docs/courses/csintro/coordinates/overview.md index 89f305bf275..24e37ddb9f8 100644 --- a/docs/courses/csintro/coordinates/overview.md +++ b/docs/courses/csintro/coordinates/overview.md @@ -1,45 +1,62 @@ # Introduction -Through math class, most middle school students are already familiar with coordinate grids and mapping x and y coordinates on a plane. To review some terms: +Let's review coordinate grids and mapping x and y coordinates on a plane: ## Axes -* The basic coordinate grid a student learns has two axes, - ->* an x-axis which runs horizontally and -* a y-axis which runs vertically. +* The basic coordinate grid has two axes: + * **x-axis** which runs horizontally (left to right), *and* + * **y-axis** which runs vertically (up to down). ## Origin -* These two axes meet at a point called the origin where both the x and the y values are zero. -* On this basic coordinate grid, the origin is in the lower left corner of the grid and has the coordinates (0,0). + +These two axes meet at a point called the origin, where both the x- and the y-values are zero. On this basic coordinate grid, the origin is in the lower left corner of the grid and has the coordinates (0,0). ## Coordinate pair -* The first value in a coordinate pair is the x value and the second value in a coordinate pair is the y value. -* A simple way to remember which value comes first is to remember their order in the alphabet. The letter x comes before the letter y in the alphabet and the x coordinate comes before the y coordinate in a coordinate pair. +The first value in a coordinate pair is the x-value and the second value in a coordinate pair is the y-value, e.g., (x,y). + +**Hint:** A simple way to remember which value comes first is to remember their order in the alphabet. The letter x comes before the letter y in the alphabet, and the x-coordinate comes before the y-coordinate in a coordinate pair. ## Coordinate value changes -* On a basic coordinate grid, +On a basic coordinate grid, ->* the value of the x coordinate increases left to right and is a measure of how many units a point is horizontally from the origin -* the value of the y coordinate increases bottom to top and is a measure of how many units a point is vertically from the origin +* The value of the x-coordinate increases from left to right and is a measure of how many units a point is horizontally from the origin. +* The value of the y-coordinate increases from bottom to top and is a measure of how many units a point is vertically from the origin. ![Math coordinates](/static/courses/csintro/coordinates/math-coords.png) -## Coordinate grid and JavaScript and the micro:bit +## Coordinate grid and the micro:bit -The 5 x 5 grid of LEDs on the micro:bit represent a coordinate grid with a horizontal x-axis and a vertical y-axis. It has an origin and you can refer to the position of the LEDs with coordinate pairs. -  -It is important however that the students understand the two major differences between the micro:bit LED grid and the coordinate grid that they are used to using in math class: -* the origin (0,0) is in the top left corner. -* the values of the y coordinates range from 0 through four and increase from top to bottom. +The 5 x 5 grid of LEDs on the micro:bit represents a coordinate grid with a horizontal x-axis and a vertical y-axis. It has an origin, and you can refer to the position of the LEDs with coordinate pairs. There are similarities and differences between the micro:bit coordinate grid and basic coordinate grids in math class. + +### Similar to coordinate grids in math +The values of the x-coordinates range from 0 through 4 and increase from left to right just as they do in the coordinate grids used in math class. + +### Different from coordinate grids in math -Note: -* The values of the x coordinates range from 0 through four and increase from left to right just as they do in the coordinate grids used in math class. +It is important, however, to understand the two major differences between the micro:bit LED grid and the coordinate grid that you might be used to from other math classes: + +* The origin (0,0) is in the top left corner. +* The values of the y-coordinates range from 0 through 4 and increase from top to bottom. ![micro:bit LED coordinates](/static/courses/csintro/coordinates/microbit-led-coords.png) ## Sidebar material ![Rene Descartes](/static/courses/csintro/coordinates/rene-descartes.jpg) -(image credit: Wikipedia Commons) +_Image credit: Wikipedia Commons_ RenÊ Descartes (1596-1650), was a French philosopher and mathematician who developed the coordinate system we use today. A story goes that while lying in bed, he noticed a fly on the ceiling. In wondering how he could describe the fly’s exact position on the ceiling, he decided to use a corner of the ceiling as a reference point and then describe the fly’s position as a measure of how far away from the reference point one would need to travel horizontally and then vertically to reach the fly. His coordinate system proved useful in many ways including creating an important link between the studies of algebra and geometry. Geometric shapes could now be described by points on a coordinate plane. + +## Knowledge Check + +**Questions:** + +1. What location is the origin on the micro:bit screen? +2. What is the range of x-values on the micro:bit screen, and in which direction do they increase? +3. What is the range of y-values on the micro:bit screen, and in which direction do they increase? + +**Answers:** + +1. (0,0) in the upper left +2. 0 to 4, increasing from left to right horizontally +3. 0 to 4, increasing from top to bottom vertically diff --git a/docs/courses/csintro/coordinates/project.md b/docs/courses/csintro/coordinates/project.md index 579e5741dbe..0ad665b790b 100644 --- a/docs/courses/csintro/coordinates/project.md +++ b/docs/courses/csintro/coordinates/project.md @@ -2,26 +2,35 @@ Use what you now know about LEDs, coordinates, and brightness to create your own project: a screensaver, or a game. You should find a way to use coordinates in your program. Even better, use variables to store and update your coordinates. -## Screensavers -One type of project is a screensaver. A long time ago, computers and televisions used cathode ray tube (CRT)screens for displays. The glass screen of the display was coated on the back with phosphor, a substance that glows when painted with electrons from an electron gun at the other end of the tube. When the same area of the screen was painted (excited) over and over again by the stream of electrons, that part of the screen would sometimes "freeze" with the same image, burned into the phosphor for good. This was called "burn-in". - -Normally, if a show was running, or if someone was actively using the computer, the display changed often enough that burn-in wasn’t a problem. Programmers learned to create a demo screen with an animation that would run whenever the screen was idle. Today, nearly all computers and television sets use LCD displays, which are not affected by burn-in. But you can still find a screen saver in nearly every computer's Settings panel, as an opportunity to show off some neat graphics or animation. - Your task is to create: * A "screen saver" animation using the plot/unplot blocks. You can fill the screen line by line, pausing between each one, or fill it with a random constellation of stars. ->-- OR -- +– OR – + +* A game that uses sprites to manage the x- and y-coordinate values of the different objects. + +Your project might use variables to store the values of sprites, which are special structures that contain an x- and a y-coordinate together that describe the sprite's location as one LED on the screen. + +## Screensavers +One type of project is a screensaver. A long time ago, computers and televisions used cathode ray tube (CRT) screens for displays. The glass screen of the display was coated on the back with phosphor, a substance that glows when painted with electrons from an electron gun at the other end of the tube. When the same area of the screen was painted (or excited) over and over again by the stream of electrons, that part of the screen would sometimes "freeze" with the same image burned into the phosphor for good. This was called **burn-in.** + +Normally, if a show was running or if someone was actively using the computer, the display changed often enough that burn-in wasn't a problem. Programmers learned to create a demo screen with an animation that would run whenever the screen was idle. Today, nearly all computers and television sets use LCD displays, which are not affected by burn-in. But you can still find a screen saver in nearly every computer's Settings panel as an opportunity to show off some neat graphics or animation. + +## Project Expectations -* A game that uses sprites to manage the x and y coordinate values of the different objects. +Make sure your project meets these specifications: -Your project might use variables to store the values of sprites, which are special structures that contain an x and a y coordinate together that describe the sprite's location as one LED on the screen. +* Uses at least three of the different kinds of 'plot', 'unplot', 'toggle', 'point x y' blocks, and uses variables to update the coordinates in some way +* Uses plotted LEDs in a meaningful way +* The program compiles and runs as intended and includes meaningful comments in code +* Provide the written Journal entry reflection (which we'll talk about after you complete your project) ## Project Ideas ### Firework screensaver -This project uses a for loop with the plot/unplot blocks to create a symmetrical design on the screen. This student used a subtraction operation to get a variable that decreases as the index variable in the loop increases. +This project uses a for loop with the plot/unplot blocks to create a symmetrical design on the screen. The following sample code uses a subtraction operation to get a variable that decreases as the index variable in the loop increases. ```sim basic.forever(() => { @@ -40,24 +49,7 @@ basic.forever(() => { }) ``` -This project uses a for loop with the plot/unplot blocks to create a symmetrical design on the screen. This student used a subtraction operation to get a variable that decreases as the index variable in the loop increases. - -```blocks -basic.forever(() => { - for (let x = 0; x <= 4; x++) { - led.plot(x, 0) - led.plot(0, 4 - x) - led.plot(4 - x, 4) - led.plot(4, x) - basic.pause(50) - led.unplot(x, 0) - led.unplot(4 - x, 4) - led.unplot(0, 4 - x) - led.unplot(4, x) - basic.pause(50) - } -}) -``` +Solution link: [Fireworks Screen Saver](https://makecode.microbit.org/_97A6Ru6LELcP) ### Cascade screensaver @@ -134,6 +126,8 @@ basic.forever(() => { speed = 10 ``` +Solution link: [Cascade](https://makecode.microbit.org/_Y2TU9cgWz07m) + ### Dodge ball game This is a Dodge Ball game that uses one sprite (dodger) to try to avoid another sprite (ball). You use the A and B buttons to move the dodger to avoid the balls that are falling from the top of the screen. @@ -199,44 +193,16 @@ ball = game.createSprite(randint(0, 5), 0) dodger = game.createSprite(2, 4) game.setScore(0) ``` +Solution link: [Dodge Ball Game](https://makecode.microbit.org/_E9b733huX2DP) + +## Journal Entry -## Reflection +Write a short journal reflection of about 150–300 words, addressing the following points: -Have students write a reflection of about 150–300 words, addressing the following points: * Did you do a screensaver? A game? Something different? How did you decide? * If you did a game, what is the object of the game? * How does your project use coordinates? * Describe something in your project that you are proud of. * Describe a difficult point in the process of designing this program, and explain how you resolved it. * What feedback did your beta testers give you? How did that help you improve your design? -  -## Assessment - -**Competency scores**: 4, 3, 2, 1 -  -### Coordinates and LEDs - -**4 =** Uses at least 3 of the different kinds of plot/ unplot/toggle/point x y blocks in a meaningful way. -`*` Uses variables to update coordinates.
-**3 =** At least 2 of the different kinds of plot/unplot/ toggle/point x y blocks in a meaningful way.
-**2 =** At least 1 of the different kinds of plot/unplot/ toggle/point x y blocks in a meaningful way.
-**1 =** No plot/unplot/ toggle/point x y blocks are implemented.    - -### micro:bit program - -**4 =** micro:bit program:
-`*` Uses plotted LEDs in a way that is integral to the program
-`*` Compiles and runs as intended
-`*` Meaningful comments in code
-**3 =** micro:bit program lacks 1 of the required elements.
-**2 =** micro:bit program lacks 2 of the required elements.
-**1 =** micro:bit program lacks all of the required elements. - -### Collaboration reflection - -**4 =** Reflection piece addresses all prompts.
-**3 =** Reflection piece lacks 1 of the required elements.
-**2 =** Reflection piece lacks 2 of the required elements.
-**1 =** Reflection piece lacks 3 of the required elements. -   -   +* Publish your MakeCode program and include the link.   diff --git a/docs/courses/csintro/coordinates/unplugged.md b/docs/courses/csintro/coordinates/unplugged.md index 384e354db40..4982f1c3f27 100644 --- a/docs/courses/csintro/coordinates/unplugged.md +++ b/docs/courses/csintro/coordinates/unplugged.md @@ -2,11 +2,11 @@ The game Battleship is perhaps the most fun a student can have practicing using a coordinate grid. The original Battleship game is a 10x10 grid with numbers on one axis and letters on the other.   -To help us practice using the correct coordinates for the grid of micro:bit LEDs, let the students play a smaller 5x5 version of Battleship using x and y coordinates instead of letters and numbers. +To help us practice using the correct coordinates for the grid of micro:bit LEDs, let's play a smaller 5x5 version of Battleship using x- and y-coordinates instead of letters and numbers.   -Have students make their own sets of 5x5 grids to reinforce the layout of the micro:bit grid. +First, make your own set of 5x5 grids to reinforce the layout of the micro:bit grid. -Each student should make two grids. One grid is for placing their own ships and keeping track of their opponent’s hits and misses and the other grid is for keeping track of their own hits and misses while trying to determine the location of their opponent’s ships. +Each player should make two grids. One grid is for placing their own ships and keeping track of their opponent’s hits and misses and the other grid is for keeping track of their own hits and misses while trying to determine the location of their opponent’s ships. ![Player Grid Example](/static/courses/csintro/coordinates/player-grid.png) Player’s grid: Mark where your ships are and keep track of your opponent’s hits and misses. @@ -31,17 +31,16 @@ Opponent’s grid: Keep track of your hits and misses while trying to locate you (0,4) (1,4) (2,4) (3,4) (4,4) ``` -Then pair the students to play against a partner. Each student's ships are hidden somewhere on their 5x5 grid. Students should be taking turns calling their shots using x and y coordinates, in the proper order. Their opponent will use those coordinates to plot the location of their shots. +Then, find someone to play Battleship with. Each person's ships are hidden somewhere on their 5x5 grid. Take turns calling your shots using x- and y-coordinates, in the proper order. Your opponent will use those coordinates to plot the location of your shots. If a hit is recorded on a ship, then you say, "Hit". If the shot misses, you say, "Miss". If the entire length of a ship is hit, it is sunk and removed from play. Tradition dictates that the player announces, "You sank my battleship!" -Since their grid is only one quarter the size of the original Battleship grid, students can use fewer and smaller ships. For example, they could play with 3 ships, one each of size 3, 2, and 1. +Since your grid is only one quarter the size of the original Battleship grid, we suggest that you use fewer and smaller ships. For example, you could play with 3 ships, one each of size 3, 2, and 1. The game can be played with just paper and pencils or you could use small tokens and markers, like coins, buttons, or paper clips to represent the ships.   ## Notes: -* Place students’ grids in sheet protectors or laminate them so they can be used again and again with white board (dry erase) markers. -* The official rules of Battleship are easily found on the internet. Modify them as needed for your particular class. +* The official rules of Battleship are easily found on the internet. Modify them as needed! ![Battleship board game](/static/courses/csintro/coordinates/battleship-board-game.jpg) The original Battleship Board Game diff --git a/docs/courses/csintro/finalproject.md b/docs/courses/csintro/finalproject.md index a164ff8659d..f8ac5875bac 100644 --- a/docs/courses/csintro/finalproject.md +++ b/docs/courses/csintro/finalproject.md @@ -2,9 +2,9 @@ ![micro:bit holder square](/static/courses/csintro/conditionals/microbit-holder.jpg) -In this unit, we will be reviewing the concepts we covered in the previous weeks, and providing some ideas for an independent final project that students can focus on in the next several weeks. We will also provide a rubric for keeping students on task and tracking the learning that they are doing as they work on their projects. This is an expanded version of the process students followed in the [Mini-Project](/courses/csintro/miniproject), in Lesson 6. +In this unit, we will be reviewing the concepts we covered in the previous weeks, and providing some ideas for an independent final project that you can focus on in the next several weeks. We will also provide a rubric for keeping yourself on task and tracking your learning as you work on your project. This is an expanded version of the process you followed in the [Mini-Project](/courses/csintro/miniproject), in Lesson 6. -Students are asked to create an independent project that demonstrates the use of something they have already learned, something they went out and researched for themselves, something they borrowed from somewhere else (with citations) and something completely original. They are also asked to document their learning process throughout the next couple of weeks using an independent project framework that emphasizes metacognitive development and process-oriented work. +You now have the opportunity to create an independent project that demonstrates the use of something you have already learned, something you went out and researched for yourself, something you borrowed from somewhere else (with citations) and something completely original. You'll also keep a journal about what you are learning over the next couple of weeks. The final project is a great way to ## Lesson plan diff --git a/docs/courses/csintro/finalproject/project.md b/docs/courses/csintro/finalproject/project.md index 96a6101184e..e8a08191992 100644 --- a/docs/courses/csintro/finalproject/project.md +++ b/docs/courses/csintro/finalproject/project.md @@ -3,6 +3,7 @@ The final project is a chance for you to use all of the skills you have been learning throughout the semester to create something that is original, and that solves a problem or serves a purpose. ## Possible ideas + * Create a game * Create something that helps somebody by solving a problem * Create something beautiful @@ -11,28 +12,35 @@ The final project is a chance for you to use all of the skills you have been lea In addition, your project code must do each of the following things: ## Show something you already know + You should demonstrate your knowledge of one or more concepts we have covered in these lessons. ## Show something new + You should demonstrate a technique, efficiency, or block that you went out and learned how to do on your own, either from the documentation, or from another classmate. ## Incorporate a maker component + You should not create a project that exists solely and independently on the micro:bit. Your project should work together with tangible components such as servos, real buttons, switches, to do something unique. ## Timeframe + Three weeks of in-class work and activities Due each week: + * 2–3 work logs * 1 Record of Thinking Due in three weeks: + * Beta testing period * Final Narrative * Final Project Code * Final project showcase and celebration at the end Assessment: + * 50% Process (initial proposal, work logs, records of thinking, final narrative) * 50% Product (project code and maker component) @@ -41,9 +49,11 @@ Teacher Note: This form of assessment places just as much weight on documenting However, you may decide to assign more or less weight to each of these pieces, and you should certainly feel free to scale up or down the documentation piece as appropriate for your classroom, grade level, and teaching priorities.   ## While working on the project + The expectation is that you are working steadily on your independent project for three weeks, testing out ideas, trying things out, getting stuck, and getting yourself unstuck. Because everyone is working on a different project, we can't assign the same homework to everybody so besides the project work itself, you are also responsible for documenting the work you are doing on the project using work logs, and reflecting on the process of your learning in a record of thinking. Here are more details on these. ## Work logs + A work log is a short, bullet point list of what you worked on, and how long it took. Stick to just the facts. It shouldn’t take more than thirty seconds or so to write up a work log. Students should do one for every class, several times a week. A shared Microsoft OneNote notebook is a great way to keep a work log that students can update regularly. Alternately, you might use a collaborative shared document, or your classroom management system, or even e-mail. Sample Work Log: @@ -57,9 +67,11 @@ Sample Work Log: **Teacher note:** We generally don't accept late work logs. If a student simply didn't have time to do any work on the project, he should still file a work log, and report that no work got done. Work logs are worth a few points each, so missing one or two isn't a problem, but if it happens a lot it's usually time to do a check-in with that student and see where she is with the project. ## Record of thinking + A Record of Thinking is like a journal entry (or like the reflection that you did for the mini-project) that tells the story of your learning throughout the past week. Go through your work logs for the week and look at what you did, where you got stuck, and how you figured it out. Then write a 150- to 300-word Record of Thinking addressing the following: + * Describe something that surprised you this week as you worked on your project. * Describe a moment where you go stuck. How did you get unstuck? * Did anyone help you this week? Who and how? @@ -75,6 +87,7 @@ I guess I would choose the word "elated" because that's what I am feeling right Teacher Note: A Record of Thinking is not an expanded work log! Students will sometimes just write a more detailed list of all of the tasks they completed over the week, and that's not the point of the Record of Thinking. The Work Logs are to show WHAT you did. The Record of Thinking is to show HOW you learned how to do it. Unlike Work Logs, I will accept late Records of Thinking as long as they come no later than the due date for the next week’s Record of Thinking. It is an important form of documentation of the learning process. ## Turning in the final project + When you turn in the final project, you should turn in your code, and a final narrative. To turn in your code, you can Share the code by clicking the Share button at the top of the MakeCode window (next to Projects). @@ -95,6 +108,7 @@ You have worked for the past three weeks to propose, design, and test an origina Please go back and read through all of your Work Logs, Records of Thinking, Beta Testing feedback, and any notes from teacher conferences.   Then, compose a comprehensive narrative that tells the story of the development of this app, and your progress toward your goals along the way.  How you tell the story is up to you, but you might consider following most, if not all, of the following questions: + * How did you start the process of designing the product/meeting your goals? * What did you hope to learn? * What challenges did you face? How did you overcome them? @@ -117,11 +131,13 @@ Sample Final Narrative: >_Once I started to get a little more clear on what to do, I was able to get more effective help from my classmates. Specifically, Jordan helped me a lot with figuring out how to get an image to display properly on the screen. He also showed me how to search through the online documentation more effectively. I think if I could do this over again, I would have scheduled more time earlier to meet with Mr. Kiang and/or found a better way to share the different online sites with my table mates because we all found different places to go. I didn’t even find out until the end that you could jump into JavaScript to make changes to the code, and it makes it all with the right blocks when you go back! (Beta Testing notes) That would have saved me a lot of time._ ## Beta testing + Beta testing is an important part of testing the final projects to uncover bugs or design issues that could make the projects difficult to use. One way to test the projects is to ask all students to come in to class on a specific day with the projects ready to test. This is not the final deadline, but projects should be "feature-complete" i.e., all features need to be incorporated into the micro:bit, and the construction of the real world elements of the project need to be done or almost done. Students can take turns presenting their projects to the entire class, or they can work in pairs to take turns trying their partner's project out and offering feedback. Students who are being critiqued should take beta testing feedback notes and turn them in as part of their final project narrative. ## Final showcase + Have a celebration of your students' hard work and hold an event at your school for parents, administrators, and other community members to appreciate all of the hard work that went into making each of the final projects. We have found that a "science fair" format works nicely, with students sitting at tables where they can demonstrate their projects and answer questions. Some schools do a "shark tank" type of event where students take turns "pitching" their project ideas to a panel composed of local software developers, entrepreneurs, and investors. Either way, a little public recognition of all of your students' hard work goes a long way! diff --git a/docs/courses/csintro/introduction.md b/docs/courses/csintro/introduction.md index d987883436d..db4494ba017 100644 --- a/docs/courses/csintro/introduction.md +++ b/docs/courses/csintro/introduction.md @@ -1,23 +1,26 @@ # Introduction -When we first started teaching computer science, we discovered two important things. We found that existing curriculum for beginners focused mostly on solving math problems or constructing geometric shapes and that there was a certain type of student that signed up for computer science classes and these students were almost always boys. We wondered whether a different approach to teaching the basics of computer programming would be more engaging and also attract a larger variety of different types of students, both boys and girls. -  -We decided to focus on what knowing how to program allowed you to do and create. Ultimately all programs are created to solve a problem or serve a purpose. The problem may be local or global, the purpose may be anything from helping doctors treat patients to pure entertainment. By starting with interesting problems the students wanted to solve, they were much more engaged in learning to code. They saw coding skills as an important part of building creative solutions. +When we first started teaching computer science, we noticed something interesting. Most of the beginner courses were focused on math problems or creating geometric shapes, and it was mostly boys signing up. That made us wonder: what if we could teach programming in a way that was more exciting and fun for everyone? -With this approach, we found that not only did we get more girls taking the course, we also got a more diverse group of boys. Opportunities for collaboration increased, and all the students got to see where their talents and skills meshed with others' interests and experiences, to make a whole that was greater than the sum of its parts. +So, we changed things up! Instead of just focusing on the math, we decided to show what programming can help you create. After all, coding is all about solving problems and making things that matter. You could use it to help doctors treat patients, make games that entertain people, or tackle big issues around the world. When our students got to work on problems they cared about, they were way more interested in learning to code. They saw programming as a super cool tool for building their own creative solutions. -We are now at the point where a third of the students taking computer science are girls, and more importantly, students are coming out of the course not only with an understanding of code, but also knowing how to read through professionally written code, and take an idea from brainstorming through prototyping to build something that matters. +And guess what? It worked! We started seeing more girls joining the class, along with boys who brought different interests and ideas. Everyone got to collaborate more, which made the class even better because each student brought something unique to the table. + +Now, about a third of the students in our computer science classes are girls, and the best part? They’re not just learning how to code—they’re learning how to think like real developers. From brainstorming ideas to building prototypes, our students are creating things that really make a difference. And that’s what this course is all about! > _- Authors Mary Kiang and Douglas Kiang_ ## Course Introduction -This is an introduction to coding and computer science by way of making and design, using the revolutionary new micro:bit microcontroller board, and Microsoft's easy and powerful MakeCode block-based coding environment. It is a project-based curriculum with a maker philosophy at its core; the idea is that by making physical objects, students create a context for learning the coding and computer science concepts. +Get ready for an awesome introduction to coding and computer science through hands-on making and design! You’ll be using the micro:bit +microcontroller board, along with Microsoft’s MakeCode, a block-based coding tool that's both easy to use and super powerful. + +In this course, you’ll be working on projects where you actually build things—real, physical objects! The idea is that by creating something you can hold in your hands, you’ll naturally learn important coding and computer science concepts. It’s all about learning by doing, and we can’t wait to see what you create! ![micro:bit man](/static/courses/csintro/microbitman.jpg) -* micro:bits may be purchased from these resellers: +* micro:bits may be purchased from resellers: -> http://microbit.org/resellers (you will need 1 micro:bit per student for this course). The "micro:bit Go Kit" includes a battery pack and USB cable as well. +> https://microbit.org/buy/ (you will need at least 1 micro:bit for this course). The "BBC micro:bit Go" includes a battery pack and USB cable as well. * Other optional suggested micro:bit accessories include: @@ -30,13 +33,13 @@ This is an introduction to coding and computer science by way of making and desi * MakeCode for the micro:bit is a free web app: https://makecode.microbit.org -Copper tape is inexpensive and super useful in all sorts of maker activities so it’s worth it to invest in a few rolls to keep on hand for micro:bit projects. We use it in [Lesson 9 (Binary Cash Register)](/courses/csintro/binary/project). You can purchase copper tape at https://www.adafruit.com/product/1128/ and https://www.sparkfun.com/products/10561. +Copper tape is inexpensive and super useful in all sorts of maker activities so it’s worth it to get a roll to keep on hand for micro:bit projects. We use it in [Lesson 9 (Binary Cash Register)](/courses/csintro/binary/project). You can purchase copper tape from vendors such as Adafruit and Sparkfun. -When students complete this course they will have a good understanding of computer science concepts that can serve as the foundation for future study. They will develop powerful design skills that they can use in future projects of all types, whether they are designing 3D printed prototypes or creating apps that serve a real world purpose. +By the time you finish this course, you'll have a solid understanding of key computer science concepts that will set you up for future learning. Plus, you'll build awesome design skills that can be used in all sorts of future projects, whether you’re creating 3D-printed prototypes or developing apps that solve real-world problems. -This course is targeted to middle school grades 6-8 (ages 11-14 years). It is also written for teachers who may not have a Computer Science background, or may be teaching an "Intro to Computer Science" course for the first time. +This course is designed for students in grades 6-8 (ages 11-14). It is designed for people who might not have a background in Computer Science or even teachers who are teaching an "Intro to Computer Science" class for the first time. So, everyone can jump in and learn together! -This course takes approximately 14 weeks to complete, spending about 1 week on each of the first 11 lessons, and 3 weeks for students to complete the final project at the end. Of course, teachers should feel free to customize the curriculum to meet individual school or district resources and timeframe. +The course takes about 14 weeks to finish, if you are working on it for a few hours a week. You’ll spend roughly one week on each of the first 11 lessons, and then you’ll need about three weeks to work on an exciting final project. But of course you can always adjust the timeline to fit your needs. This course is flexible and designed to work for you! ## Overall Course Scope & Sequence: @@ -51,37 +54,33 @@ This course takes approximately 14 weeks to complete, spending about 1 week on e 9. [Bits, bytes, and binary](/courses/csintro/binary) 10. [Radio](/courses/csintro/radio) 11. [Arrays](/courses/csintro/arrays) -12. [Independent final project](/courses/csintro/finalproject) +12. [Accelerometer](/courses/csintro/accelerometer) +13. [Independent final project](/courses/csintro/finalproject) -Each of the 12 lessons is comprised of the following parts: +Each lesson is made up of the following parts: * Topic Introduction -* Unplugged Activity (30 min) Ėļ An offline game or activity that demonstrates the concept/topic -* micro:bit Activity (45-60 min) Ėļ- An activity that everyone makes on their micro:bit that teaches the skills learned in this lesson. -* Project (60-120 min) Ėļ- A prompt for an original project that each student will create to demonstrate their understanding of the skills and concepts covered in this lesson. -* Project Mods Ėļ Examples of additional things students can do to extend the project -* Assessment Ėļ- A project rubric and guidance for grading the project. -* Standards Ėļ -A list of [CSTA K-12 Computer Science Standards](https://www.csteachers.org/?page=CSTA_Standards) and/or concepts covered by this lesson. +* micro:bit Activity (45-60 min) An activity that everyone makes on their micro:bit that teaches the skills learned in this lesson. +* Project (60-120 min) A prompt for an original project that you can create to practice the skills and concepts covered in this lesson. +* Project Mods: Examples of additional things you can do to extend the project +* Standards: A list of [CSTA K-12 Computer Science Standards](https://www.csteachers.org/?page=CSTA_Standards) and/or concepts covered by this lesson. ### Topic introduction -The introduction to each lesson will tell you what learning objectives are covered in the lesson, and presents an overview of that lesson's topic. Some lessons have a specific activity that can help introduce the topic to students in a fun way. - -### Unplugged activity (30 min) -Each lesson starts with an unplugged activity, which doesn't require a computer or a micro:bit. It's a chance to get students up and moving around, and is designed to be a fun introduction to the computer science concept covered in that lesson. Unplugged activities are an important way to demonstrate new concepts in a tangible, often kinesthetic, way. Since so many computer-based topics are abstract, unplugged activities are very effective at fostering understanding that students will then demonstrate in later activities. +The introduction to each lesson will tell you what learning objectives are covered in the lesson, and presents an overview of that lesson's topic. ### micro:bit activity (45–60 min) -Each lesson also contains a micro:bit activity, which we informally refer to as a "birdhouse" activity, after the innumerable wooden birdhouses so many of us made in wood shop as a way to master basic skills. Each lesson's micro:bit activity is an example that walks students step-by-step through building a project that demonstrates that lesson's topic. By the time students finish the activity, they will have written code that they can use in a different project of their own design. +Each lesson contains a micro:bit activity, which is an example that walks you step-by-step through building a project that demonstrates that lesson's topic. By the time you finish the activity, you will have written code that you can use in a different project of their own design. -Some students will finish the activity more quickly than others. Those students can then be a helpful resource for their classmates, or they can challenge themselves by modifying, or "modding" the activity to do something different. We have provided examples and suggestions at the end of many of these activities, and feel free to suggest your own (or encourage your students to come up with their own ideas!) +You can always challenge yourself by modifying, or "modding" the activity to do something different. We have provided examples and suggestions at the end of many of these activities, and feel free to come up with your own! ### Project (60–120 min) -After presenting the concept in an unplugged fashion, then walking students through a demonstration activity, it is time to challenge students to use those skills to create something that is creative and original. Students will be working on their projects in a "collaboratively independent" way, which means each student is responsible for turning in his or her own project, but are encouraged to work together and help each other while doing so. Some form of reflection is an important part of documenting the learning that has taken place, and it's a great idea to share out the final projects and reflections, either at an event or on a blog. +After the demonstration activity, it is time to challenge yourself to use those same skills to create something that is creative and original. This is where the real learning takes place because instead of following step by step instructions, you will prove to yourself that you can use those skills in a new way to create something that is personal and unique. There are also a series of Project Mods that students can do to extend the project they have created. These are useful for students who already have some experience with coding or who want an extra challenge. -### Assessment -A rubric is provided for each project that can be customized according to what students are being asked to demonstrate. For the Activities we just expect students to do them, so those are fairly simple to check off. For the Projects, however, there is often a range of grades based on how closely the project meets the specifications of the assignment. +### Journal +From time to time we will ask you to write down your reflections in a journal. Keeping a personal journal is a powerful tool! It helps you track your progress, organize your thoughts, and see how much you've grown over time. By writing down what you learned from each activity and project, you’ll deepen your understanding and notice patterns in your problem-solving process. Plus, it’s a great way to celebrate your successes and identify areas for improvement, making you a more thoughtful and confident learner! ### Standards Where applicable, we have mapped each of the lessons to the [Computer Science Teachers Association (CSTA) K-12 Standards](https://www.csteachers.org/?page=CSTA_Standards), which are US nationally recognized standards for computer science education. diff --git a/docs/courses/csintro/iteration.md b/docs/courses/csintro/iteration.md index 46dfb86d452..be45efc52d4 100644 --- a/docs/courses/csintro/iteration.md +++ b/docs/courses/csintro/iteration.md @@ -2,11 +2,12 @@ ![Guitar Picture](/static/courses/csintro/iteration/guitar.jpg) -This lesson introduces the concept of looping and iteration. Presents the 'While' block as a combination of an iteration and a conditional statement. +This unit introduces the concept of iteration—or ways to make things repeat. In MakeCode, this is accomplished with loop blocks. You will learn to code with three types of loop blocks as well as sprite and music blocks. In the project, you'll code your own unique program using loops, variables, and other blocks you've explored and learned. You'll design and build an object that uses sound, display, and motion in some way. To incorporate sound and motion, additional materials such as micro-servo motors (a small motor) and crocodile clips are recommended for Lessons B and C. ## Lesson objectives -Students will... +You will... + * Understand the value of iteration in programming * Understand looping as a form of iteration * Learn how and when to use the Looping blocks ‘repeat’, ‘while’, and ‘for’ @@ -15,23 +16,17 @@ Students will... ## Lesson structure * Introduction: Lather. Rinse. Repeat. -* Unplugged Activity: Walk a Square pseudocode * micro:bit Activities: Code a sprite to walk a Square, travelling light, micro:bit alarm! * Project: Get Loopy! * Project Mods: Use servo motors to add a motion element to the project -* Assessment: Rubric +* Reflection: Write a short journal entry * Standards: Listed ## Lesson plan 1. [**Overview**: Iteration and looping](/courses/csintro/iteration/overview) -2. [**Unplugged**: Walk a square](/courses/csintro/iteration/unplugged) -3. [**Activity**: Loops demos](/courses/csintro/iteration/activity) -4. [**Project**: Get loopy](/courses/csintro/iteration/project) - -## Flipgrid - -The [Flipgrid](https://info.flipgrid.com/) topic for the **Iteration** lesson: https://flipgrid.com/ee559ab7 +2. [**Activity**: Loops demos](/courses/csintro/iteration/activity) +3. [**Project**: Get loopy](/courses/csintro/iteration/project) ## Related standards diff --git a/docs/courses/csintro/iteration/activity.md b/docs/courses/csintro/iteration/activity.md index 106c395a366..a0c1c342f9d 100644 --- a/docs/courses/csintro/iteration/activity.md +++ b/docs/courses/csintro/iteration/activity.md @@ -1,317 +1,242 @@ # Activity: Loops demos -Microsoft MakeCode has three different loop blocks: -* 'Repeat' block -* 'While' block -* 'For' block +For this lesson's coding activities, we'll use three different loop blocks in Microsoft MakeCode: -To start, the students can code the same algorithm they created in the unplugged activity using a loop. +* 'repeat' block – This block repeats the code n number of times. +* 'while' block – This block runs the code as long as the condition inside of it is true. +* 'for' block – This block repeats the code n number of times but with a variable. -## ‘Repeat’ block -Code a Sprite to walk a square. Have students click on the Loops category in the Toolbox, and look at the three choices available. - -![Loops category](/static/courses/csintro/iteration/loops-category.png) +We'll do three coding activities to demonstrate how each type of loop block works. -The very first one is the ‘repeat’ block! Have students drag the repeat block to the coding Workspace. They’ll notice that this block takes a **parameter**. +## Coding activity 1: Code a sprite to walk a square with the 'repeat' loop +In this example, you'll be coding Sprite to walk in a square. -A **parameter** is a type of variable used as input to a function or routine. In this case, the parameter tells the repeat block how many times we want the code within the block to repeat. +* In Microsoft MakeCode, start a new project and name it something like: **Sprite walking square**. You can leave the 'on start' block in the coding Workspace but can delete the 'forever' loop block. + +![Loops category](/static/courses/csintro/iteration/loops-category.png) -For now, we’ll leave the parameter at 4. +* On start, we want the sprite to appear. To make this happen, go to the Variables Toolbox drawer, and select the “Make a Variable” button. Name the new variable *sprite* and select OK. Now, in the Variables Toolbox drawer, drag a 'set sprite to' block to the coding Workspace and drop it inside the 'on start' block. -To create a **sprite** that will walk a square: +### Create a Sprite -* Click on the Advanced category in the Toolbox. This will open up a more advanced menu of blocks. -* Click on Game category, and drag a ‘create sprite’ block to the coding workspace. +* Select the *Advanced* category at the bottom of the Toolbox. This will open up more of the Toolbox menu. Select the Game category and drag a 'create sprite' oval block to the coding Workspace. Drop it in the 'set sprite' block, replacing 0. ![Game category](/static/courses/csintro/iteration/game-category.png) -* We’ll need two more blocks from the Game menu. Referring to their ‘Walk a Square’ pseudocode, see if the students can find the blocks they need for moving their sprite and turning their sprite. -* Drag out a ‘move by’ block and a ‘turn right by’ block. -* They now have these blocks in their coding workspace. -* For this project, they can delete the default ‘forever’ block. - -```block -let sprite: game.LedSprite = null -sprite = game.createSprite(2, 2) -``` -```block -let sprite: game.LedSprite = null -sprite.move(1) -``` -```block -let sprite: game.LedSprite = null -sprite.turn(Direction.Right, 45) -``` - -Time to fix those default parameter values! -* We want our sprite to start in the top left corner of the micro:bit screen, so change the parameters for both **x** and **y** to zero. -* To make the sprite move from one side of the screen to the other (as though walking around a chair), change the move by parameter to **4**. -* To make the sprite turn to walk a square, change the ‘turn right by’ degrees to **90**. For now, it's OK to leave the sprite turning right instead of left as we did in our pseudocode. +You should now see the 'sprite', or a red LED light, appear in the middle of the micro:bit simulator. -Your blocks now look like this: +On the face of the micro:bit is a 5 x 5 grid of LED lights. The X coordinates are the horizontal light positions that go from 0-4, and the Y coordinates are the vertical light positions that go from 0-4 as well. We can see from the code blocks that we've created our sprite at X, Y position (2, 2). If we want to start our sprite in the top left of the screen, we'll have to change the starting coordinates to (0, 0). -```block -let sprite: game.LedSprite = null -sprite = game.createSprite(0, 0) -``` -```block -let sprite: game.LedSprite = null -sprite.move(4) -``` -```block -let sprite: game.LedSprite = null -sprite.turn(Direction.Right, 90) -``` +### Moving the Sprite -Notice that the blocks are all grayed out. That’s because we have not yet attached them to any event handlers. +Now let's make our sprite move around the face of the micro:bit. We'll activate this when we press a button. -* On start, we want the sprite to appear. To make this happen, go to the Variables menu, create a new variable called 'sprite', and drag a ‘set sprite to’ block to the coding window. -* Place the ‘set sprite block’ into the ‘on start’ block. -* Attach the ‘create sprite’ block to the ‘set sprite’ block +* From the Input Toolbox drawer, drag a 'on button pressed' block onto the workspace.

We'll need two more blocks from the Game menu. Referring to the pseudocode, see if the you can find the blocks you need for moving your sprite forward and turning your sprite. +* From the Game Toolbox drawer, drag out a 'sprite move by' block and a 'sprite turn right by' block to the coding Workspace and drop into the 'on button pressed' block. +* To make the sprite move from one side of the screen to the other (as though walking around a chair), we'll need to move the sprite 4 places. So, change the value in the 'sprite move by' block from 1 to 4. +* To make the sprite turn to walk a square, change the 'turn right by' degrees from 45 to 90. For now, it's OK to leave the sprite turning right instead of left as we did in our pseudocode. -```blocks -let sprite: game.LedSprite = null -sprite = game.createSprite(0, 0) -``` +### Using the 'Repeat' block -You should now see the sprite appear in the top left of the micro:bit simulator. +Following our pseudocode, we could add three more Move and Turn blocks to make our sprite walk a square, but there is an easier, more efficient way to code this! By using a Repeat loop. -* To add more control for when our sprite moves, drag a ‘on button A pressed’ block from the Input menu. -* Place the ‘repeat’ block into the ‘on button A pressed’ block -* Place the ‘move by’ block into the ‘repeat’ block -* Place the ‘turn right by’ block into the ‘repeat’ block just under the ‘move by’ block. +* Select the Loops category in the Toolbox. Drag the 'repeat' block to the coding Workspace and place it around the sprite 'move by' and 'turn' blocks. -```blocks -let sprite: game.LedSprite = null -sprite = game.createSprite(0, 0) +Notice that the 'repeat' block contains a default value of 4. This means that it will repeat whatever blocks of code it contains four times. -input.onButtonPressed(Button.A, () => { -   for (let i = 0; i < 4; i++) { -       sprite.move(4) -       sprite.turn(Direction.Right, 90) -   } -}) -``` -Go ahead and run the program. Make the sprite move by pressing button A. +Go ahead and run the program. Make the sprite move by pressing button A in the simulator. -What happened? Did you see the sprite move? No? +What happened? Did you see the sprite move? No? Why? Because it happens so quickly, you can't see the sprite appear. -## Slo-Mo -A helpful feature of Microsoft MakeCode is "Slo-Mo", or slow-motion mode. -* Click on the snail icon under the micro:bit simulator. -This will slow down the execution (running) of the program, and highlight parts of your code so you can see step-by-step, which line of code is being processed. +### Use Debug Mode -![micro:bit sim in slo-mo](/static/courses/csintro/iteration/slo-mo.gif) +A helpful feature of Microsoft MakeCode is **Debug Mode.** Select the bug icon under the micro:bit simulator. This will halt the execution (running) of the program and allow you to press the Step button to run your program line by line. It will also highlight parts of your code so you can see at each step which line of code is being processed. -Now run your program several more times. Do you see the different lines of your code highlighted as the program runs? Do you see the sprite move? +Now, run your program several more times. Do you see the different lines of your code highlighted as the program runs? Do you see the sprite move? -![Slo-Mo in blocks](/static/courses/csintro/iteration/slo-mo-blocks.png) -Slo-Mo in Blocks +### Add a pause -![Slo-mo-in JavaScript](/static/courses/csintro/iteration/slo-mo-javascript.png) -Slo-Mo in JavaScript +So, the code is running and the sprite is moving! Sometimes we forget just how fast computers are. So that we can see the sprite move even in “regular” mode, let's add a pause to our program right after each time the sprite moves. This will give our human eyes a chance to see it move. -So, the code is running and the sprite is moving! Sometimes we forget just how fast computers are. So that we can see the sprite move even in ‘regular’ mode, lets add a pause to our program right after each time the sprite moves. This will give our human eyes a chance to see it move. +Select the bug icon again to turn off Debug Mode. -* Click the snail icon again to turn off Slo-Mo. -* From the Basic Toolbox category, drag a ‘pause’ block to the coding window and add it to our ‘repeat’ block right after the ‘turn right by’ block. +* From the Basic Toolbox category, drag a 'pause' block to the coding window and add it to our 'repeat' block right after the 'turn right by' block. -Your final program should look like this: +Solution link: [Sprite Walking a Square](https://makecode.microbit.org/_D3k3ydYj28VY) -```blocks -let sprite: game.LedSprite = null -input.onButtonPressed(Button.A, () => { -   for (let i = 0; i < 4; i++) { -       sprite.move(4) -       sprite.turn(Direction.Right, 90) -       basic.pause(100) -   } -}) -sprite = game.createSprite(0, 0) -``` +Download and run your program on the micro:bit. Now we can see the sprite move. It still moves pretty quickly, but at least we can see it move. -Run your program again. Now we can see the sprite move. It still moves pretty quickly, but at least we can see it move. +> Optional Mod +> +> Try experimenting with changing the Pause value, the number of times to Repeat, or the number of spaces to move the Sprite to see how these changes affect your program. -If there is time, let the students experiment with changing the parameters to see how these changes affect their program. +## Coding activity 2: Code a traveling light with 'for' loops -We just used the first of the 3 different types of Loop blocks available to us. What about the other 2 loop blocks, ‘while’ and ‘for’? +Now we'll move on to code with the 'for' loop block. The 'for' block is useful when you have a variable in your loop that you want to change by a fixed amount within a specific range each time through a loop. What does this mean? Let's look at an example. -## ‘For’ block: traveling light +Let's make an LED light move across the entire micro:bit display from left to right, top row to bottom row. -The ‘for’ block is useful when you have a variable in your loop that you want to change by a fixed amount within a specific range each time through a loop. What does this mean? Let’s look at an example. +### Pseudocode -Let’s make an led light move across the entire display from left to right, top row to bottom row. +Our pseudocode for the first row might look like: -Our pseudocode for the first row might look like this: ``` -Turn led x:0, y:0 on +Turn led (x:0, y:0) on Pause -Turn led x:0, y:0 off +Turn led (x:0, y:0) off Pause -Turn led x:1, y:0 on +Turn led (x:1, y:0) on Pause -Turn led x:1, y:0 off +Turn led (x:1, y:0) off Pause -Turn led x:2, y:0 on +Turn led (x:2, y:0) on Pause -Turn led x:2, y:0 off +Turn led (x:2, y:0) off Pause -Turn led x:3, y:0 on +Turn led (x:3, y:0) on Pause -Turn led x:3, y:0 off +Turn led (x:3, y:0) off Pause -Turn led x:4, y:0 on +Turn led (x:4, y:0) on Pause -Turn led x:4, y:0 off +Turn led (x:4, y:0) off ``` -That’s a lot of code, most of it repeated. Perfect for a loop. + +That's a lot of code, and most of it repeats. It's perfect for a loop! * What is the only variable that is changing in this pseudocode? _The value of the x coordinate_. * How much is the value of the x coordinate changing each time? _The value of the x coordinate is changing by 1 each time_. * What is the range of values for the x coordinate? _The range of values for the x coordinate is 0 through 4_. -Now let’s code! - -* From the Loops Toolbox drawer, drag a ‘for’ block to the coding workspace. -* Since we’ll be changing the value of the x coordinate, make a new variable, named **xindex**. -* We’ll plot and unplot the leds to turn them on and off. From the Led Toolbox drawer, drag a 'plot' block and an 'unplot' block to the coding workspace. -* From the Basic Toolbox drawer, drag two ‘pause’ blocks to the coding workspace. -* Place the following blocks into the ‘for’ block: the ‘plot’ block, a ‘pause’ block, the ‘unplot’ block, the second 'pause' block. -* Place the ‘for’ block inside a forever block. - -```block -basic.forever(() => { -   for (let index = 0; index <= 4; index++) { -       led.plot(0, 0) -       basic.pause(100) -       led.unplot(0, 0) -       basic.pause(100) -   } -}) -``` +Now let's code! -Let’s look at the parameters. - -* Change the ‘index’ in the ‘for’ block to the ‘xindex’ variable we made. -* Change the value of the x coordinates in the plot and unplot blocks to this same variable. - -```blocks -let index = 0 -basic.forever(() => { -   for (let xindex = 0; xindex <= 4; xindex++) { -       led.plot(xindex, 0) -       basic.pause(100) -       led.unplot(xindex, 0) -       basic.pause(100) -   } -}) -``` +### Create variables to hold the x and y position -We can use the default values for the rest of the parameters. +The first thing we'll want to do is create some Variables to hold the X and Y position values. -You should now see a light moving from left to right along the top row of the micro:bit simulator. +* Create a new project and name it something like: **Traveling light.** Then, select the Variables Toolbox drawer and select the **Make a Variable** button. +* Name the new variable something like: **Xvalue**. Then, create another variable and name it something like: **Yvalue**.

+Notice that these variable blocks now appear in the Variables Toolbox drawer. Now, we need to set the starting value for the x and y variables to be 0. +* From the Variable Toolbox drawer, drag two 'set (variable)' blocks onto the Workspace and drop into the 'on start' block. Then, in one of the 'set (variable)' blocks, use the dropdown menu to select the 'Xvalue' variable. -```sim -let index = 0 -basic.forever(() => { -   for (let xindex = 0; xindex <= 4; xindex++) { -       led.plot(xindex, 0) -       basic.pause(100) -       led.unplot(xindex, 0) -       basic.pause(100) -   } -}) -``` +### Code the loop for the x values -To make our pattern continue through all the leds, we can change the value of the y coordinate as well. - -To do this efficiently, using the fewest lines of code, we can even put a loop inside a loop. Loops inside other loops are known as **nested loops**. - -* So that we can change the value of the y coordinate, make a new variable, named **yindex**. -* Drag out another ‘for’ block from the Loops Toolbox drawer. -* Place this new ‘for’ block around our original ‘for’ block, all within the forever block. -* Change the ‘index’ in the outer ‘for’ block to the ‘yindex’ variable we made. -* Change the value of the y coordinates in the plot and unplot blocks to this same variable. - -```blocks -let index = 0 -let yindex = 0 -basic.forever(() => { -   for (let yindex = 0; yindex <= 4; yindex++) { -       for (let xindex = 0; xindex <= 4; xindex++) { -           led.plot(xindex, yindex) -           basic.pause(100) -           led.unplot(xindex, yindex) -           basic.pause(100) -       } -   } -}) -``` +* From the Loops Toolbox drawer, drag a 'for' block to the coding Workspace and drop it into the 'forever' loop. +* Instead of the default 'index' variable, we're going to use the 'Xvalue' variable that we created. From the Variables Toolbox drawer, drag the 'Xvalue' variable block out onto the Workspace and drop it into the 'for loop', replacing the 'index' block.

In this way, each time we iterate through the 'for' loop, our 'Xvalue' variable will increment its value—starting from 0 and going up to 4. +* We'll plot and unplot the LED lights to turn them on and off. From the Led Toolbox drawer, drag a 'plot' block and an 'unplot' block to the coding Workspace. Drop both into the 'for' loop. +* From the Basic Toolbox drawer, drag two 'pause' blocks to the coding Workspace. Drop them in the 'for' loop—one after the 'plot' block, and one after the 'unplot' block. This will slow things down a bit so we can see the lights turning on and off. +* Change the value of the x coordinates in the 'plot' and 'unplot' blocks to the x coordinate value from the 'for' loop. From the Variables Toolbox drawer, drag two 'Xvalue' variable blocks onto the Workspace and drop one each into the x coordinate of the 'plot' block, and the x coordinate of the 'unplot' block. -There! With only a half dozen or so lines of code, we have made our light travel through all the coordinates on the micro:bit screen. - -```sim -let index = 0 -let yindex = 0 -basic.forever(() => { -   for (let yindex = 0; yindex <= 4; yindex++) { -       for (let xindex = 0; xindex <= 4; xindex++) { -           led.plot(xindex, yindex) -           basic.pause(100) -           led.unplot(xindex, yindex) -           basic.pause(100) -       } -   } -}) -``` +Now, you should see a light moving from left to right along the top row of the micro:bit simulator! -**Check:** Make sure the students can read this code. +### Code the loop for the y values -Here is what is happening to the values of the x & y coordinates as the program steps through each line and loop inside the forever block: +To make our pattern continue through all the LEDs, we can change the value of the Y coordinate as well. To do this efficiently using the fewest lines of code, we can put a loop inside a loop. Loops inside other loops are known as nested loops. -1. In the outer of the two for loops, the value of the y-coordinate is set to 0. -2. The nested inner loop then sets the value of the x-coordinate to zero. -3. The corresponding led (x:0, y:0) is plotted and then unplotted. -4. Then the value of the x-coordinate is increased by 1 and step #3 runs again with the coordinates now (x:1, y:0). -5. Then the value of the x-coordinate is increased by 1 again and step #3 runs again with the coordinates now (x:2, y:0). -6. The inner loop keeps running like this until it has completed its loop with the value of the x coordinate now 4. -7. With the inner loop complete, the program now runs the second iteration of the outer loop, increasing the value of the y-coordinate by 1, then back to the inner loop which runs 4 more times stepping through values for x from 0 through 4. - -Have the students use the Slo-Mo mode to watch the program step through the loops. +* Drag out another 'for' loop block from the Loops Toolbox drawer and place it around our original 'for' block, all within the 'forever' block. +* From the Variables Toolbox drawer, drag out the 'Yvalue' variable block and drop into the outer 'for' loop, replacing the default 'index' variable. +* Change the value of the y coordinates in the 'plot' and 'unplot' blocks to this same variable (just like we did for the x coordinate earlier). + +### Solution and discussion + +Solution link: [For Loop](https://makecode.microbit.org/_Kf4hF0PHPaYz) + +Be sure that you can explain the complete code in words: + +Here is what is happening to the values of the x and y coordinates as the program steps through each line and loop inside the 'forever' block: + +1. In the outer of the two for loops, the value of the y coordinate is set to 0. +2. The nested inner loop then sets the value of the x coordinate to 0. +3. The corresponding led at (x:0, y:0) is plotted and then unplotted. +4. Then the value of the x coordinate is increased by 1, and step 3 runs again with the coordinates now at (x:1, y:0). +5. Then the value of the x coordinate is increased by 1 again, and step 3 runs again with the coordinates now at (x:2, y:0). +6. The inner loop keeps running like this until it has completed its loop with the value of the x coordinate now at 4. +7. With the inner loop complete, the program now runs the second iteration of the outer loop, increasing the value of the y coordinate by 1, then back to the inner loop which runs 4 more times stepping through values for x from 0 through 4. + +## Knowledge Check -* By the end of the program run, how many times has the inner loop executed? 25 -* Other than knowing that there are 25 LEDs and each is lit up once, how can you figure this out? ->_The outer loop loops 5 times altogether, once for every value of the y coordinate from 0 through 4. Each time the outer loop runs, the inner loop runs 5 times, once for every value of the x coordinate from 0 through 4. 5 runs of the outer loop x 5 runs of the inner loop = 25 times the inner loop executes._ +### Use Debug to count loops: -## Mods -* If there is time, let the students experiment with changing the parameters to see how these changes affect their program. -* What happens if you switch the positions of the nested loops, so the outer loop loops through the xindex values and the inner loop loops through the yindex values? -* What happens if you remove the ‘unplot’ block and the ‘pause’ block below it? +Use Debug Mode to watch the program step through the loops. Then, answer the following questions: + +1. By the end of the program run, how many times has the inner loop executed? +2. Other than knowing that there are 25 LEDs and each is lit up once, how else can you figure this out? + +**Answers:** + +1. 25 +2. The outer loop loops 5 times altogether, once for every value of the y coordinate from 0 through 4. +Each time the outer loop runs, the inner loop runs 5 times, once for every value of the x coordinate from 0 through 4. **5** runs of the outer loop **x 5** runs of the inner loop **= 25 times** the inner loop executes. + +> Mods: +> +> Experiment with changing the parameters to see how these changes affect your program: +> +> * What happens if you switch the positions of the nested loops, so the outer loop loops through the xindex values and the inner loop loops through the yindex values? +* What happens if you remove the 'unplot' block and the 'pause' block below it? -## ‘While’ block: micro:bit alarm! -The while block is useful when you want your program to loop until a certain event happens or a different condition is met. - -For example, maybe you want an alarm to sound if someone shakes your micro:bit! -In order to turn the alarm off, you press the button A. Until you press the button, the alarm should continue to sound! - -You can use a 'while' block with a nested ‘repeat’ block like this: - -```blocks -input.onGesture(Gesture.Shake, () => { -   while (!(input.buttonIsPressed(Button.A))) { -       for (let i = 0; i < 2; i++) { -           music.playTone(262, music.beat(BeatFraction.Half)) -           music.playTone(523, music.beat(BeatFraction.Half)) -       } -   } -}) -``` +## Coding activity 3: Code a micro:bit alarm with a 'while' loop + +The 'while' block is useful when you want your program to loop until a certain event happens or a different condition is met. For example, maybe you want an alarm to sound if someone shakes your micro:bit. In order to turn the alarm off, you press the button A. Until you press the button, the alarm should continue to sound. You can use a 'while' block with a nested 'repeat' block. + +### The 'while' loop + +* Create a new project in MakeCode and name it: Alarm Clock. Delete the default 'on start' and 'forever' blocks from the coding Workspace. Then from the Input Toolbox drawer, drag an 'on shake' block onto the Workspace. +* From the Loops Toolbox drawer, drag a 'while' block to the Workspace and drop it inside the 'on shake' block + +Notice that there is a condition attached to the 'while' loop. Recall from the previous lesson that a conditional expression may evaluate to true or false. In this case, the default block always evaluates to true, so this 'while' loop will repeat forever! + +* *Have you come across another block that loops over and over forever?* **Answer:** the 'forever' block + +### Alarm 'on shake' + +We'll come back to the while loop condition. For now, let's code our alarm sound. + +* From the Music Toolbox drawer, drag two 'play tone' blocks to the coding Workspace and drop them inside the 'while' loop. +* In the 'play tone' blocks, use the dropdown menu to change the beat value to 'ÂŊ' a beat. In the second 'play tone' block, change the tone from 'Middle C', to 'High C'. + +Try your code in the Simulator. What happens when you shake the micro:bit? **Warning:** You may want to turn down the volume on your computer! -* Can you read what this code does? -* Can you write out pseudocode that describes what this code does? +Our alarm goes off, and because the 'while' loop repeats continuously, there's no way to turn off our alarm! -Example Pseudocode: +### Turn off alarm when press button + +Let's add a condition to our 'while' loop that will turn off the alarm when the user presses a button. + +* From the Logic Toolbox drawer, scroll down to the Boolean section. Drag a 'not' hexagon block to the workspace. Drop it in the 'while' loop, replacing . +* From the Input Toolbox drawer, drag the 'button A is pressed' hexagon-shaped block onto the Workspace and drop it into the 'not' block in the 'while' loop. + +**Hint:** Make sure you hover the 'button A is pressed' hexagon over the 'not' hexagon until only the empty hexagon shows the yellow outline so you don't replace the entire 'not' hexagon. + +Now, our 'while' loop will only repeat as long as button A has not been pressed. + +### Complete Code + +Solution link: [Alarm Clock](https://makecode.microbit.org/_2cxF9yfCc1Ax) + +Test the code in the simulator. + +## Knowledge Check + +**Questions:** + +1. Match the following types of loops with their definitions:
+**Loops:**
for, while, repeat
+**Definitions:**
Runs a command n times
Runs a command n times with a variable to increment each time
Runs a command as long as a certain condition is met. +2. How could you rewrite this pseudocode with loops? + +``` +Step forward +Turn left +Step forward +Turn left +Step forward +Turn left +Step forward +Turn left +``` -_When someone shakes the micro:bit, while button A is not pressed, play the two tone alarm twice. Keep playing the alarm tones until the user presses the A button._ +**Answers:** -To use sound with your micro:bit, you will need to connect it to some speakers or headphones. See how to do this here: [Hack you headphones](/projects/hack-your-headphones). +1. **Repeat loop:** Runs a command n times; **For loop:** Runs a command n times with a variable to increment each time; **While loop:** Runs a command as long as a certain condition is met +2. Repeat 4 times: Step forward, Turn left \ No newline at end of file diff --git a/docs/courses/csintro/iteration/overview.md b/docs/courses/csintro/iteration/overview.md index 83a0c1234ed..fc3a4ecb8d2 100644 --- a/docs/courses/csintro/iteration/overview.md +++ b/docs/courses/csintro/iteration/overview.md @@ -1,15 +1,13 @@ # Introduction -In computer programming, iteration is the repetition of a sequence of code. A loop is a form of iteration. A loop repeats code until a certain condition is met. - -## Questions for the students: -* Do you use shampoo to wash your hair? _Most will say ‘Yes’_. -* Have you ever read the instructions on a bottle of shampoo? _Most will say ‘No’_. +In computer programming, **iteration** is the repetition of a sequence of commands. Computer programmers can use a special type of code called a loop around the commands they want to repeat as a form of iteration. A **loop** repeats code until a certain condition is met. -Most of us have never read the instructions on a bottle of shampoo, because we already know how to use shampoo. +Most of us use shampoo to wash our hair, yet have never read the instructions on a bottle of shampoo, because we already know how to use it. What algorithm could you write for shampooing your hair? + Example: + 1. Wet hair. 2. Apply shampoo to wet hair 3. Scrub shampoo into hair @@ -23,18 +21,21 @@ How does this one extra step affect the algorithm? In computer programming, this is known as the ‘shampoo algorithm’ and is an example of a loop. It is also an example of an ‘infinite’ or ‘endless’ loop as the algorithm keeps repeating with no condition that ends the looping. ![Iteration cartoon](/static/courses/csintro/iteration/iteration-cartoon.png) -DBwebsolutions.com +_DBwebsolutions.com_ ‘Rinse. Repeat.’ has even become a meme and made its way into modern song lyrics. What other common activities involve repetitive actions? _Examples: Singing (choruses repeat), dancing, school cheers, walking and running, exercise routines..._ -## Optional -Share with your students the history of ‘Lather, Rinse, Repeat.’ +## "Lather, Rinse, Repeat" + +See the below content for interesting applications of "repeat" loops: Lather, Rinse, Repeat: Hygiene Tip or Marketing Ploy By Lauren Goldstein October 11, 1999 -http://archive.fortune.com/magazines/fortune/fortune_archive/1999/10/11/267035/index.htm + +[https://money.cnn.com/magazines/fortune/fortune_archive/1999/10/11/267035/]() + (FORTUNE Magazine) – In Benjamin Cheever's novel The Plagiarist, a marketing executive becomes an industry legend by adding one word to shampoo bottles: REPEAT. He doubles shampoo sales overnight. This bit of fiction reflects a small yet significant eddy of U.S. consumer angst: If we REPEAT, are we or are we not playing into the hands of some marketing scheme? It turns out that in real life there's a reason you should repeat, or at least there used to be. In the 1950s, when shampoos began to be mass-marketed, we didn't wash our hair all that often--once or twice a week, as opposed to five times a week as most of us do now. Also, we used a lot more goop in our hair. It was the age of Brylcream and antimacassars, remember. Paul Wallace, the director of hair-care research and development for Clairol, says that when cleaning agents in shampoo came up against that amount of oil and goop, "it depressed the lather." A second application was needed to get the suds that consumers expected. Lots of suds mean that hair is already clean. Maybe too clean (there's no oil to break through), but consumers like it. @@ -44,10 +45,11 @@ FORTUNE asked Frederic Fekkai, the noted and notably expensive New York City hai At any rate, Wallace says advances in shampoo technology mean that only one application of, for instance, Clairol's Herbal Essences is sufficient to break through the oiliest hair. The company has stricken the use of both REPEAT and REPEAT IF DESIRED from all Clairol products. Yet a lot of brands, like Suave by Unilever and L'Oreal, still say REPEAT. Others, like Unilever's Finesse and Revlon's Flex, opt for the less imperative REPEAT IF DESIRED. Procter & Gamble uses REPEAT IF NECESSARY on Pantene. Getting consumers to wash twice can, of course, increase sales--in ways one might not imagine. Double sudsing leads to dry hair, Fekkai points out, and that means more beauty products! "When you do two shampoos, even if you don't usually use a conditioner, you have to use a little," he says. "The conditioner becomes very important." REPEAT. FOLLOW WITH CONDITIONER. Words Cheever's marketer could have retired on. ---Lauren Goldstein + +-- Lauren Goldstein ![Shampoo bottle](/static/courses/csintro/iteration/shampoo.png) -From Wikipedia (https://en.wikipedia.org/wiki/Lather,_rinse,_repeat): +From Wikipedia ([https://en.wikipedia.org/wiki/Lather,_rinse,_repeat](https://money.cnn.com/magazines/fortune/fortune_archive/1999/10/11/267035/)): Lather, rinse, repeat (sometimes wash, rinse, repeat) is an idiom roughly quoting the instructions found on many brands of shampoo. It is also used as a humorous way of pointing out that such instructions if taken literally would result in an endless loop of repeating the same steps, at least until one runs out of shampoo. It is also a sarcastic metaphor for following instructions or procedures slavishly without critical thought. diff --git a/docs/courses/csintro/iteration/project.md b/docs/courses/csintro/iteration/project.md index be2be8857a7..17ac4b0fe88 100644 --- a/docs/courses/csintro/iteration/project.md +++ b/docs/courses/csintro/iteration/project.md @@ -2,25 +2,37 @@ ![Birthday Card Project](/static/courses/csintro/iteration/birthday-card.jpg) -There are many different ways to use the three types of loop blocks. +In this project, you will create a program with loops, variables, and parameters, then design and build an object that uses the micro:bit program and sound, display, and motion in some way. + +## Project expectations + +Make sure your project meets these specifications: + +* Use at least three different loops in a meaningful way +* Use unique variable names that clearly describe what the variable values hold +* Use sound, display, and motion in a way that’s integral to the program +* The program should compile and run as intended and include meaningful comments in the code +* Provide the written Reflection Diary entry (which we’ll talk about after you complete your project) + +## Project ideas + +There are many different ways to use the three types of loop blocks. Consider following questions to prompt your brainstorming process: -Recall the different common repetitive actions you thought of back at the beginning of this lesson. * How will you use loops to create something useful, entertaining, or interesting? * What might you make? -Here are some suggestions: +Some project suggestions: + * Create an animated gif (looping image that changes) and add music that matches. -* Create animation that repeats for one of the melodies included in Make Code  (like Happy Birthday). +* Create an animation that repeats for one of the melodies included in MakeCode (like Happy Birthday). * Create different animations that run when different buttons are pressed. * Create an alarm that includes sound and images. What will set the alarm off? What will make the alarm stop sounding? * Use servo motors to create a creature that dances and changes its expression while a song plays. -## Example +### Project example: Hat Man ![Hat Man](/static/courses/csintro/iteration/hatman.png) -Hat Man Project - -### Hat Man Videos +_Hat Man Project_ [**micro:bit Hat Man**](https://youtu.be/Xvybu_T5IL8) https://youtu.be/Xvybu_T5IL8 @@ -29,58 +41,16 @@ https://youtu.be/Xvybu_T5IL8 [**micro:bit Hat Man - inside view**](https://youtu.be/ZfKgFQjygQQ) https://youtu.be/ZfKgFQjygQQ
+ This project uses the micro:bit light sensor to display a happy face when it is sunny, and a frowning face when it is dark. The micro:bit is connected to a servo mounted on the inside of the container, and the smile and frown are attached to plastic coffee stirrers with tape and hot glue. -## Reflection +## Journal Entry + +Write a short reflection of about 150–300 words, addressing the following points: -Have students write a reflection of about 150–300 words, addressing the following points: * Explain how you decided on your particular "loopy" idea. What brainstorming ideas did you come up with? -* What type of loop did you use? For, While, or Repeat +* What type of loops did you use? For, While, and/or Repeat * What was something that was surprising to you about the process of creating this program? * Describe a difficult point in the process of designing this program, and explain how you resolved it. * What feedback did your beta testers give you? How did that help you improve your loop demo? - -## Assessment - -**Competency scores**: 4, 3, 2, 1 - -### Loops - -**4 =** At least 3 different loops are implemented in a meaningful way.
-**3 =** At least 2 loops are implemented in a meaningful way.
-**2 =** At least 1 loop is implemented in a meaningful way.
-**1 =** No variables are implemented. - -### Variables (parameters) - -**4 =** All variable names are unique and clearly describe what information values the variables hold
-**3 =** The majority of variable names are unique and clearly describe what information values the variables hold.
-**2 =** Few variable names are unique or clearly describe what information values the variables hold.
-**1 =** None of the variable names clearly describe what information values the variables hold. - -### Sound, display, and motion - -**4 =** Uses sound, display, and motion in a way that is integral to the program.
-**3 =** Uses only two of the required elements in a way that is integral to the program.
-**2 =** Uses only one of the required elements in a way that is integral to the program.
-**1 =** None of the required elements are used. - -### micro:bit program -**4 =** micro:bit program:
-`*` Uses loops in a way that is integral to the program
-`*` Compiles and runs as intended
-`*` Meaningful comments in code
-**3 =** micro:bit program lacks 1 of the required elements.
-**2 =** micro:bit program lacks 2 of the required elements.
-**1 =** micro:bit program lacks 3 or more of the required elements. - -### Collaboration reflection -**4 =** Reflection piece includes:
-`*` Brainstorming ideas
-`*` Construction
-`*` Programming
-`*` Beta testing
-**3 =** Reflection piece lacks 1 of the required elements.
-**2 =** Reflection piece lacks 2 of the required elements.
-**1 =** Reflection piece lacks 3 of the required elements. - +* Publish your MakeCode program and include the link. \ No newline at end of file diff --git a/docs/courses/csintro/iteration/unplugged.md b/docs/courses/csintro/iteration/unplugged.md index 82b703444e5..203992fb259 100644 --- a/docs/courses/csintro/iteration/unplugged.md +++ b/docs/courses/csintro/iteration/unplugged.md @@ -2,19 +2,18 @@ ![Chair with Pseudocode on the board.](/static/courses/csintro/iteration/chair-pseudo.png) -## Objective -To reinforce the concept of iteration by having students act out the repeated steps of an algorithm in real life. +## Your Task -## Overview -Students will give the teacher instructions to do a simple activity, then look for places where using iteration could shorten their code and make it more efficient. +In this activity, you’ll guide yourself step-by-step through a simple exercise, then figure out how to make your instructions shorter and more efficient by using a loop. -## Process +## WHat You'll Do + +* Place a chair in front of you. +* Stand at the back right side of the chair, facing forward. +* Now, think about what instructions you would need to give yourself in order to walk around the chair and end up exactly where you started. If you’re not sure, try walking around the chair once to get an idea of the steps. + +##Writing the Instructions -* Place a chair in the front of the room. -* Stand at the back right side of the chair facing the students. -* Ask the students what instructions they could give you that when followed would lead you to walk around the chair, ending up just as you started. You may want to demonstrate what this would look like by walking around the chair. -* Tell the students you can only process one instruction at a time, so their algorithm needs to be step-by-step. -* As students suggest instructions write them on the board or wherever everyone can see them. Their pseudocode will probably end up looking something like this: 1. Step forward 2. Turn left 3. Step forward @@ -24,24 +23,31 @@ Students will give the teacher instructions to do a simple activity, then look f 7. Step forward 8. Turn left ![Square walking pattern](/static/courses/csintro/iteration/square-walk.png) -* Go ahead and follow their algorithm to prove that it works. But that’s eight lines of code! Tell students that the same instructions can be written using just three lines of code. If they have not noticed already, have students look for places where the code repeats. -* Tell them that whenever you have code that repeats, you have an opportunity to use a loop to simplify your code. -* Prompts: ->* What lines are repeated? _(1) Step forward. (2) Turn left_. ->* How many times are they repeated? Four ->* So how could we rewrite this code? Students will suggest a version of the following: ->_Repeat 4 times: Step forward, Turn left_ -* Go ahead and follow their revised algorithm to prove that it works. -There! They have just rewritten eight lines of code as three lines of code, by using a loop. -The ‘repeat’ command creates a loop. The code within the loop gets repeated a certain number of times until a condition is met. The condition in this algorithm is that the code in the loop is repeated 4 times. Once this condition is met, the program exits the loop. +That's eight lines of instructions for you to follow! + +## Make It More Efficient + +Now, look at the instructions you wrote. Do you notice any steps that repeat? + +Hint: What actions do you keep doing? (1) Step forward. (2) Turn left. How many times do you repeat these steps? (Answer: Four times). + +Here’s the key: Whenever you see repeated steps, you can use a loop to simplify your instructions. How could you rewrite your directions? It might look like this: + +Repeat 4 times: Step forward, Turn left + +## Test the New Instructions -This is a great opportunity to have the students think of the benefits of having fewer lines of code. _Some possible reasons: Less typing, saves time, fewer chances of making a mistake, easier to read the code, fewer lines of code to debug..._ +Now, follow your new, shorter set of instructions. Did it work? Great! You’ve just taken eight lines of instructions and turned them into three by using a loop! -## Notes -* Depending on the particular class, you can make this exercise more challenging, by requiring the students to be more specific in their instructions. +## Why Fewer Instructions Matter -**Example:** Step forward 14 inches (you can have students actually measure the exact distance), turn left 90 degrees... - +Think about why using fewer instructions can be helpful. Some reasons could be: +* It saves time +* It’s less to write down +* There’s less chance of making mistakes +* It’s easier to read +* It’s faster to find and fix problems +You’ve just learned how to make your instructions (or code) more efficient by using loops! \ No newline at end of file diff --git a/docs/courses/csintro/making.md b/docs/courses/csintro/making.md index 512c8d829a2..2a3ebb46a5d 100644 --- a/docs/courses/csintro/making.md +++ b/docs/courses/csintro/making.md @@ -1,30 +1,23 @@ # Making with micro:bit -This Lesson introduces the micro:bit as a piece of hardware that has a specific size and weight, and -generally must be supported and incorporated as an essential component of a tangible artifact. Focus -on incorporating the physical micro:bit into a basic making activity. +This lesson introduces the design thinking process as a way to design something that meets someone else's needs. By focusing on building the micro:bit into a pysical object, you'll gain experience in working with a piece of hardware that has a specific size and weight, and that needs to be supported and held securely. ![micro:bit board](/static/courses/csintro/making/microbit-board.png) ## Lesson objectives -Students will... +You will... * Exercise creativity and resourcefulness by coming up with ideas for using simple household materials to accommodate the micro:bit’s size and weight in many different ways. -* Test and iterate using different materials and sizes in order to create an optimal design to house the micro:bit and battery pack +* Test and iterate using different materials and sizes in order to create an optimal design to house the micro:bit and battery pack. * Learn how to download programs and move them to the micro:bit file to run on the micro:bit. * Use the design thinking process to develop an understanding for a problem or user need. -* Apply their understanding in a creative way by making a “micro:pet” creature. +* Apply your understanding in a creative way by making a “micro:pet” creature. ## Lesson plan -* [**Introduction**: The micro:bit is for making](/courses/csintro/making/introduction) -* [**Unplugged**: Design Thinking](/courses/csintro/making/unplugged) -* [**Activity**: MakeCode download](/courses/csintro/making/activity) -* [**Project**: micro:pet (including mods and rubric)](/courses/csintro/making/project) - -## Flipgrid - -The [Flipgrid](https://info.flipgrid.com/) topic for the **Making** lesson: https://flipgrid.com/5773f935 +1. [**Introduction**: The micro:bit is for making](/courses/csintro/making/introduction) +2. [**Activity**: MakeCode download](/courses/csintro/making/activity) +3. [**Project**: micro:pet (including mods and rubric)](/courses/csintro/making/project) ## Related standards diff --git a/docs/courses/csintro/making/activity.md b/docs/courses/csintro/making/activity.md index aa81a09f8b7..7cb9d3773f6 100644 --- a/docs/courses/csintro/making/activity.md +++ b/docs/courses/csintro/making/activity.md @@ -4,9 +4,9 @@ **Objective:** Learn how to download programs from the MakeCode tool. -**Overview:** Students will create a simple program in Microsoft MakeCode and download it to their micro:bit using a USB cable. +**Overview:** You will create a simple program in Microsoft MakeCode and download it to your micro:bit using a USB cable. -For this activity, students will each need a micro:bit, a micro-USB cable, a computer, and a battery pack. +For this activity, You will need a micro:bit, a micro-USB cable, a computer, and a battery pack. ![micro:bit kit](/static/courses/csintro/making/microbit-kit.jpg) @@ -26,13 +26,13 @@ basic.forever(() => { }) ``` -At the bottom of of the editor, name the project as "Happy Sad Face" and click on the disk icon to save the project. +At the bottom of of the editor, name the project "Happy Sad Face" and click on the disk icon to save the project. ![Save file](/static/courses/csintro/making/happy-sad-file.jpg) Now, click on **Home** to go back to the home screen. -At the right of **My Projects** on the home screen, click on the **Import** button and then click on **Import File** in the import dialog. Select the file that you just saved to your computer in the previous step. +At the right of **My Projects** on the home screen, click on the **Import** button and then click on **Import File** in the import dialog. Select the file that you just saved to your computer in the previous step. ![Import button](/static/courses/csintro/making/import-button.png) @@ -86,6 +86,6 @@ To move the program to your micro:bit, drag the downloaded "microbit-xxxx.hex" f The micro:bit will hold one program at a time. It is not necessary to delete files off the micro:bit before you copy another onto the micro:bit; a new file will just replace the old one. -For the next project, your students should attach the battery pack (it takes 2 AAA batteries) to the micro:bit using the white connector. That way they can build it into their design without having to connect it to the computer. +For the next project, attach the battery pack (it takes 2 AAA batteries) to the micro:bit using the white connector. That way you can build it into your design without having to connect it to the computer. ![Battery pack](/static/courses/csintro/making/battery-pack.jpg) diff --git a/docs/courses/csintro/making/introduction.md b/docs/courses/csintro/making/introduction.md index afdf220f51b..8b8e58f8e58 100644 --- a/docs/courses/csintro/making/introduction.md +++ b/docs/courses/csintro/making/introduction.md @@ -1,12 +1,13 @@ # Introduction -The micro:bit is a great way to teach the basics of programming and computer science. The Microsoft MakeCode block-based coding environment is a powerful and intuitive way to make the micro:bit react to all sorts of input, and you can introduce fundamental concepts such as iteration, conditional statements, and variables using MakeCode. +The micro:bit is an awesome way to start learning how to code and understand the basics of computer science. You’ll be using the Microsoft MakeCode platform, which lets you create programs by dragging and dropping blocks of code—kind of like building with digital LEGO pieces. It’s easy to use, and with it, you can make the micro:bit +respond to different inputs and learn important coding concepts like loops (doing something over and over), if-then statements (making decisions in code), and variables (storing and using information). -Students often focus primarily on the 5x5 LED screen for providing output. Although this is the most directly accessible way to see a reaction to some kind of input, there are many more creative possibilities when you encourage your students to see the micro:bit as a “brain” that can control physical, tangible creations. +A lot of people focus on the micro:bit's 5x5 LED display because it’s a quick way to see your program in action. But there’s so much more you can do! Think of the micro:bit as the “brain” behind all kinds of physical creations. It can control things you build in real life—not just what happens on its tiny screen. -These creations don’t have to be complex or highly technical. It’s great to have students building with common household supplies. Because the micro:bit is so lightweight, and supports so many sensors, it can be incorporated easily into a physical design as long as students plan ahead for its size and weight. One of the first questions you might ask students is “Where does the micro:bit fit in your creation?” +Your creations don’t need to be super complicated. You can make awesome stuff using everyday materials like cardboard, paper, or whatever else you have around. Since the micro:bit is lightweight and has lots of built-in sensors, it’s easy to add to your designs. Just think ahead about where the micro:bit will fit in your project. -In this first lesson’s project, we focus on making something creative that features the micro:bit as its “face”. We purposely start this course with a lesson on Making and the physical nature of the micro:bit, because it is important to set the tone for the whole course that this is a class about making, building, crafting and construction. It helps if you have an art room available where kids can work, or arts and crafts supplies in your classroom that kids can use to build. +For this first project, we’re going to make something creative where the micro:bit acts as the “face” of your animal. We’re starting this way to show that this class is all about making, building, and creating things with your hands. If you have an art room or crafting supplies nearby, that’s perfect! You’ll be able to use those to bring your ideas to life. Some common making supplies to gather: @@ -21,4 +22,49 @@ Some common making supplies to gather: * string * markers -![Maker materials](/static/courses/csintro/making/maker-materials.png) \ No newline at end of file +![Maker materials](/static/courses/csintro/making/maker-materials.png) + +# Activity: Design Thinking + +![Design thinking](/static/courses/csintro/making/design-thinking.png) + +Note: The project later in this unit will use the work you have done in this activity, so it's important not to skip it. + +**Objective:** To introduce a process of design that starts with talking to another person. Whatever you build with code should serve a purpose or fill a need. Sometimes what you build will make the world more beautiful, or help somebody else. Our design process, based on a process called design thinking, will give you a specific framework for thinking purposefully about design. + +**Overview:** In this activity, you will interview a friend or a family member about their ideal pet. You should take notes. The first step in coding by design involves understanding someone else’s needs. Then, you can create prototypes that get you closer and closer to the best solution. + +**Materials:** A partner, and something to take notes on + +**Getting started:** +The goal of this activity is to gather information from your partner that will help you to design a micro:bit pet for your partner. + +**5 minutes:** Interview your partner. The goal is to find out what your partner considers to be their ideal pet. You should mostly listen, and ask questions to keep your partner talking for the entire time. Here are some questions to start with: + +* Do you have a pet? What is it? +* What do you like about your pet? What do you dislike? +* Is there anything you wish your pet could do? Why? +* Tell me about your ideal pet. + +The goal is to find out more about your partner by asking questions. Try to ask “Why?” as much as possible. Your partner will tell you about his or her ideal pet, but you are really finding out more about your partner’s likes and dislikes. When we design, we create real things for real people. So we need to start with understanding them first. + +**5 minutes:** Review your notes, and circle anything that seems as if it will be important to understanding how to create the ideal pet for your partner. Circle ideas, advice, anything that could be helpful when you start building. Then, you should use what you have discovered about your partner to fill in the blanks: + +"My partner needs a `__________________` because `__________________`." + +This definition statement should draw some conclusions about your partner's need based on the conversation you have had with that person. + +**5 minutes:** Sketch at least 5 ideas of pets that would meet your partner's needs. Stick figures and diagrams are okay. At this point, quantity is more important than quality. You shouldn't limit yourself to real animals; unicorns and mashups are totally fine! + +Make sure you keep your notes and sketches! You will use them in the project for this lesson. + +## Examples + +![Design thinking sketch 1](/static/courses/csintro/making/dt-sketch1.jpg) + +![Design thinking sketch 2](/static/courses/csintro/making/dt-sketch2.jpg) + +![Design thinking sketch 3](/static/courses/csintro/making/dt-sketch3.jpg) + +![Design thinking sketch 4](/static/courses/csintro/making/dt-sketch4.jpg) + diff --git a/docs/courses/csintro/making/project.md b/docs/courses/csintro/making/project.md index 7f124e2b637..38558b39276 100644 --- a/docs/courses/csintro/making/project.md +++ b/docs/courses/csintro/making/project.md @@ -2,9 +2,9 @@ ## Project -This project is an opportunity for students to create a micro:pet for the partner they interviewed in the Unplugged activity. They should review their notes and try to summarize what their partner finds appealing in a pet. Then, they should use whatever materials are available to create a prototype of a pet their partner would like. +This project is an opportunity for you to create a micro:pet for the partner you interviewed in the Unplugged activity. You should review your notes and try to summarize what your partner finds appealing in a pet. Then, you should use whatever materials are available to create a prototype of a pet your partner would like. -We often ask students to sketch a few designs on paper first, then consult with their partner to see which aspects of those designs they find most appealing. The purpose of prototyping is to gather more feedback to help you in your final design (“I like this part from Idea A, and I like this part from Idea Bâ€Ļ”) +It might make sense to sketch a few designs on paper first, then consult with your partner to see which aspects of those designs they find most appealing. The purpose of prototyping is to gather more feedback to help you in your final design (“I like this part from Idea A, and I like this part from Idea Bâ€Ļ”) Build a micro:pet that: * Matches your partner’s needs @@ -14,6 +14,7 @@ Build a micro:pet that: Your design should use whatever materials are available to support the micro:bit so that its face is showing. You can be creative and decide how to mount the board, and how to decorate your critter. Think about the following questions when you construct it: + * Will it be an animal? A plant? A robot? A bug? * Will it have any moving parts? * If it moves, how can you hold the micro:bit securely? @@ -27,43 +28,34 @@ Some photos of sample micro:pets below! * Create a way to carry your animal. * Create an animal that reacts when you pet it or move it (find a way to detect when the micro:bit is moved or when its position changes in a certain way.) -## Reflection -Have students write a reflection of about 150–300 words, addressing the following points: -* Summarize the feedback you got from your partner on your idea. How would you revise your design, if you were to go back and create another version? -* What was it like to have someone designing a pet for you? Was it a pet you would have enjoyed? Why or why not? What advice did you give them that might help them redesign? -* What was it like to interview your partner? What was it like to be listened to? +## Journal Entry +After you've completed your project, take some time to write in your design journal! You might write about some of the following: + +* What feedback did you get from your partner on your idea? How would you revise your design, if you were to go back and create another version? +* What was it like to design a pet for someone else? Was it a pet they would have enjoyed? Why or why not? What advice did they give you that might help you redesign? +* What was it like to interview your partner? What was it like to have to take the time to purposely listen to someone else? * What was something that was surprising to you about the process of designing the micro:pet? * Describe a difficult point in the process of designing the micro:pet, and explain how you resolved it. -## Rubric -For creative projects such as these, we normally don’t use a qualitative rubric to grade the creativity or the match with their partner’s needs. We just check to make sure that the micro:pet meets the required specifications: -* Program properly downloaded to micro:bit -* micro:bit supported so the face is showing -* micro:bit can be turned on and off without taking critter apart -* Turned in notes on interview process -* Written reflection (prompt is above) - ## micro:pet Examples ![A dog micro:pet](/static/courses/csintro/making/micropet-dog.jpg) -Dog +_Dog_ https://youtu.be/2ZCDB-a_uRY -micro:pet Fish Tank - +_micro:pet Fish Tank_ ![A piggy bank micro:pet](/static/courses/csintro/making/micropet-piggy-bank.jpg) -Pink Piggy +_Pink Piggy_ ![A ladybug micro:pet](/static/courses/csintro/making/micropet-ladybug.jpg) -Ladybug +_Ladybug_ ![A caterpiller micro:pet](/static/courses/csintro/making/micropet-caterpillar.jpg) -Caterpillar +_Caterpillar_ ![A fox micro:pet](/static/courses/csintro/making/micropet-fox.jpg) -Fox +_Fox_ ![A robot micro:pet](/static/courses/csintro/making/micropet-robot.jpg) -Robot - +_Robot_ diff --git a/docs/courses/csintro/miniproject.md b/docs/courses/csintro/miniproject.md index f663489689e..4c97dc3508f 100644 --- a/docs/courses/csintro/miniproject.md +++ b/docs/courses/csintro/miniproject.md @@ -2,17 +2,15 @@ ![Ideas](/static/courses/csintro/miniproject/problem-solving.png) -In this unit, we will be reviewing the concepts we covered in the previous weeks, and providing some ideas for an independent “mini-project” students can focus on in the next several classes. We will also introduce a framework for keeping students accountable to the work they are doing individually and in groups, and providing a rubric for assessment of the development process, as well as the finished product. +In this unit, we’re going to review the stuff we’ve covered over the past few weeks, and give you some ideas for an independent “mini-project” that you’ll work on in the next few sessions. We’ll also show you a framework to help you stay on track with your work and give you some structure to guide your progress. -It is important to allow students to practice accounting for the work they are doing on a short “mini-project” like this, so that when they move on to an independent project spanning multiple weeks, it will be easier for you to keep track of what everybody is doing. - -It also reinforces the important idea that how you solve problems is at least as important to learning as whether you solved them at all (or even got the right answer). Programming is a process of patient problem-solving, and finding ways to value, acknowledge, and reward the problem-solving process is an important part of assessment. +This project is designed to remind you that how you solve problems is just as important as getting the right answer. Programming is all about being patient and working through challenges, and it's really important to recognize and reward the effort and thinking you put into solving problems—not just whether you got it right! ## Lesson plan 1. [**Review**: Looking back at what we've learned so far](/courses/csintro/miniproject/review) -3. [**Activity**: Collaboratively independent](/courses/csintro/miniproject/activity) -4. [**Project**: Mini-project](/courses/csintro/miniproject/project) +2. [**Project**: Mini-project](/courses/csintro/miniproject/project) +3. [**Activity**: Collaboratively Independent (Tips for teachers)](/courses/csintro/miniproject/activity) ## Related standards diff --git a/docs/courses/csintro/miniproject/activity.md b/docs/courses/csintro/miniproject/activity.md index c24695f6434..7a20e4d63ed 100644 --- a/docs/courses/csintro/miniproject/activity.md +++ b/docs/courses/csintro/miniproject/activity.md @@ -1,5 +1,8 @@ # Activity: Collaboratively independent +## Tips for teachers +These tips are just for teachers who are assigning the mini-project to a classroom of students. + Teachers want their students to collaborate on projects but they also want to be able to hold them accountable for getting their work done. Many teachers struggle with assessing exactly how much each individual contributed to a group project, as well as making sure that everyone does his or her “fair share”. The Mini-Project (and the Final Project) are not group projects. Students are asked to propose their own independent project and are expected to get it done. But they are not on their own in this process! We build in frequent opportunities for students to collaborate and share the collective knowledge of the class as they go. We ask them to be “collaboratively independent.” diff --git a/docs/courses/csintro/miniproject/project.md b/docs/courses/csintro/miniproject/project.md index 4b5ede709b4..8d83e7503c9 100644 --- a/docs/courses/csintro/miniproject/project.md +++ b/docs/courses/csintro/miniproject/project.md @@ -1,27 +1,13 @@ ## Project: Mini-Project -This project takes approximately a week to complete. Most of that time is spent working on the project in a makerspace or art classroom. +This project should take approximately a week to complete. -The mini-project is an opportunity for students to design a project that serves a purpose by solving a problem or filling a need. It is also an opportunity to do two things: +The mini-project is an opportunity for you to design a project that serves a purpose by solving a problem or filling a need. It is also an opportunity to do two things: * Show what you know * Learn something new -Ideally, there should be a maker component to this project. This is a real world component that works with the code on the micro:bit to do something unique. - -Students are asked to each propose an original independent project. Students are allowed to work on the same idea, but they cannot turn in the same code. They can, and should work collaboratively, solving the same kinds of problems together, but the projects they turn in should be unique and original. - -## Showcasing student work - -Students will be showing their work regularly to each other in informal ways. Think about also organizing a day or an evening when parents, administrators, or others from the community are invited to come and view the students' projects. - -We find that a "science fair" type of setup works well here, with students stationed at their own tables, showing off and demonstrating their project. An event like this works well for these reasons: - -* A real world audience for the work students have done can be very motivating -* It is a chance for people who are not familiar with the micro:bit to appreciate the finished product -* It provides good feedback to students about how someone interacts with their product -* It is a chance to have real conversations with the people behind the product, rather than just viewing the product on display by itself -* Finally, and most importantly, it is a chance to bring the community together to celebrate the great work all of your students have done! +Ideally, there should be a maker component to this project. This is a real world component that works with the code on the micro:bit to do something unique. ## Assignment @@ -49,15 +35,13 @@ We find that a "science fair" type of setup works well here, with students stati View projects at the following sites for inspiration: -* http://make.techwillsaveus.com/bbc-microbit -* http://microbit.org/ideas/ -* https://twitter.com/MicroMonstersUK +* https://microbit.org/projects/make-it-code-it/ * [Projects](/projects) ## Examples ![Toss the ball project](/static/courses/csintro/miniproject/toss-the-ball.jpg) -Toss the Ball +_Toss the Ball_ This is a skill game in which an aluminum foil ball is thrown into a plastic cup. Copper tape lining the sides and bottom of the cup completes the circuit when the ball touches it. @@ -73,9 +57,9 @@ This is a prototype of a storybook that could use the micro:bit to display anima ## Work logs -Because students are working on the projects in class, and much of the benefit comes from working together to solve problems, they should account for the work they are doing by writing a work log. +Because much of the benefit of completing a mini-project like this comes from working through problems, you should consider keeping a work log. -A work log is a short, bullet point list of what they worked on, and how long it took. Stick to the facts. It shouldn’t take more than thirty seconds or so to write up a work log. Students should do one for every class. A shared Microsoft OneNote notebook is a great way to keep a work log that students can update regularly. Alternately, you might use a collaborative shared document, or your classroom management system, or even e-mail. +A work log is a short, bullet point list of what you worked on, and how long it took. Stick to the facts. It shouldn’t take more than thirty seconds or so to write up a work log. Try to do one for every class. A shared Microsoft OneNote notebook is a great way to keep a work log that can be updated regularly. ### Sample Work Log >**_April 11_**
@@ -84,66 +68,17 @@ _0 min. Talked with Mr. Kiang about how to attach wires so they won’t fall off _20 min. Put target back together with pins_
_10 min. Helped Cody with attaching his scoreboard_ -## Reflection +## Journal Entry -At the end of the week, students should compose a final reflection that summarizes the process of their learning over the course of the week. They should go back through their work logs and talk about the following: +At the end of the week, compose a final journal reflection that summarizes the process of your learning over the course of the week. You should go back through your work and discuss the following: * Talk about one challenge you faced in creating this project, either a challenge in coding or in making the artifact. How did you overcome this challenge? * What did you demonstrate that you already knew? * What was the new thing you learned in order to make this? How did you learn about it? -* Who in the class provided help to you along the way? How? +* Was anyone particularly helpful to you along the way? Who and how? * Describe one specific thing you are proud of in this project. * What would you do differently next time? * If you had another week to work on this project, what might you add or improve? Sample Reflection (excerpt) >_“I spent this week finishing up little details with my program, making it work better and more user friendly. The part that surprised me the most was the little things that kept popping into my head, little suggestions that could potentially be good to add, but might not be necessary or even useful. At the beginning of the assignment, I just added them as quickly as I thought of them, but as the project neared the midpoint and conclusion, I find myself considering if I actually need them (as previous additions have been since quickly deleted). Another thing that I find interesting about this is that it is a rather specialized project. Not many people would use it except for me. However, this is supposed to be easily used by other people, so I have to take them into consideration as I design the project. I also realized that I had, at some point, broken part of my code without realizing it, so I now have to fix part of it. The reason that it is a problem is because I added a lot of code at once without deleting it, which is unfortunate. Next time I will add small amounts of code and test it first.”_ - -## Assessment - -**Competency scores**: 4, 3, 2, 1 - -### Code - Show what you know - -**4 =** Code very effectively demonstrates the use of previous concept(s). Variable names are unique and clearly describe what information values the variables hold. Code is highly efficient.
-**3 =** Code only partially demonstrates previous concepts, and/or is not efficient.
-**2 =** Code only partially demonstrates previous concepts, and/or is not efficient, variable names not clear.
-**1 =** Code does not demonstrate previous concepts, is not efficient, variable names not clear. - -### Code - Show something new - -**4 =** Code very effectively demonstrates the use of new concept(s). Variable names are unique and clearly describe what information values the variables hold. Code is highly efficient.
-**3 =** Code only minimally demonstrates new concepts, and/or is not efficient.
-**2 =** Code only minimally demonstrates new concepts, and/or is not efficient, variable names not clear.
-**1 =** Code does not demonstrate new concepts, is not efficient, variable names not clear.
- -### Maker component - -**4 =** Tangible component is tightly integrated with the micro:bit and each relies heavily on the other to make the project complete.
-**3 =**Tangible component is somewhat integrated with the micro:bit but is not essential.
-**2 =** Tangible component does not add to the functionality of the program.
-**1 =** No tangible component. - -### Work Logs - -**4 =** All work logs submitted on time, and accurate.
-**3 =** One late or missing work log and/or work logs not accurate nor sufficiently detailed.
-**2 =** Two late or missing work logs and/or work logs not accurate nor sufficiently detailed.
-**1 =** More than two late or missing work logs and/or not accurate nor sufficiently detailed. - -### Reflection - -**4 =** Reflection piece describes:
-`*` Development Process
-`*` Something new
-`*` Something proud of
-`*` Future mods
-**3 =** Reflection piece lacks 1 of the required elements.
-**3 =** Reflection piece lacks 2 of the required elements.
-**1 =** Reflection piece lacks 3 of the required elements. -  -## Notes - -We actually split the grading of code between "show what you know" and "show something new." If a student uses variables incorrectly or uses bad variable names, we generally would take off points in both places. Sometimes it is difficult to distinguish between what is old and new if the student's reflection is less than clear; in those cases, we have to use some discretion in terms of where we take points off. Another option would be to break out the Variables category into its own row. - -As always, these rubrics are just a starting point and you should certainly feel free to adjust them as appropriate for your own classroom or learning environment. diff --git a/docs/courses/csintro/miniproject/review.md b/docs/courses/csintro/miniproject/review.md index 36ca98be49b..b0b660673ab 100644 --- a/docs/courses/csintro/miniproject/review.md +++ b/docs/courses/csintro/miniproject/review.md @@ -22,4 +22,4 @@ Conditional statements tell the computer when to do something. They are used to ## Iteration and looping -Portions of your code can be made to run over and over by using a Repeat or a For block loop. This allows you to iterate over several different variables, or items in a group, and do something to each of them. You can also combine a conditional statement and a loop by using a While block, which will repeat until a certain condition becomes true. \ No newline at end of file +Portions of your code can be made to run over and over by using a Repeat or a For block loop. This allows you to iterate over several different variables, or items in a group, and do something to each of them. You can also combine a conditional statement and a loop by using a While block, which will repeat until a certain condition becomes true. \ No newline at end of file diff --git a/docs/courses/csintro/radio.md b/docs/courses/csintro/radio.md index 641e42f4622..ffb67dfb064 100644 --- a/docs/courses/csintro/radio.md +++ b/docs/courses/csintro/radio.md @@ -2,18 +2,19 @@ ![Combo Box Example](/static/courses/csintro/radio/combo-box.png) -This lesson covers the use of more than one micro:bit to share and combine data. Students will explore a complex epidemiological program (Infection) that demonstrates the Radio functionality of the micro:bit. Students will send and receive numbers and strings in a series of guided activities. Finally, students are asked to collaborate so that they can share their micro:bits and create a project together. +This lesson covers the use of more than one micro:bit to share and combine data. You will send and receive numbers and strings in a series of guided activities, then create a project that makes use of the micro:bit's powerful Radio blocks. + +**Please note that this lesson is centered around the micro:bit's communication capabilities, so testing the code in this lesson will require two micro:bits.** ## Lesson objectives -Students will... +You will... * Understand how to use the Radio blocks to send and receive data between micro:bits * Understand the specific types of data that can be sent over the Radio ## Lesson structure * Introduction: Radio & communication -* Unplugged Activity: Infection simulation * micro:bit Activity: Marco Polo & Morse Code * Project: Radio * Assessment: Rubric @@ -22,13 +23,8 @@ Students will... ## Lesson plan 1. [**Overview**: Radio and communications](/courses/csintro/radio/overview) -2. [**Unplugged**: Infection simulation](/courses/csintro/radio/unplugged) -3. [**Activity**: Marco Polo and Morse code](/courses/csintro/radio/activity) -4. [**Project**: Radio project](/courses/csintro/radio/project) - -## Flipgrid - -The [Flipgrid](https://info.flipgrid.com/) topic for the **Radio** lesson: https://flipgrid.com/eb9af729 +2. [**Activity**: Marco Polo and Morse code](/courses/csintro/radio/activity) +3. [**Project**: Radio project](/courses/csintro/radio/project) ## Related standards diff --git a/docs/courses/csintro/radio/activity.md b/docs/courses/csintro/radio/activity.md index 4d9d1e67c32..bb586664d8a 100644 --- a/docs/courses/csintro/radio/activity.md +++ b/docs/courses/csintro/radio/activity.md @@ -1,32 +1,30 @@ -# Activity: Marco Polo and Morse code +# Coding Activity 1: Marco Polo ![Marco Polo Cartoon](/static/courses/csintro/radio/marco-polo.png) -Guide the students in creating programs that use the radio communication blocks to send and receive data between two micro:bits. +Marco Polo was the first Westerner to journey to Eastern Asia and document his travels. There is an American game called "Marco Polo", which is a form of call-and-response tag played in a swimming pool. One person closes their eyes and calls "Marco", and the other players must respond "Polo." Using the sound of their voices only, the Marco player must find and tag the Polo players. -Notes: -* When using the radio blocks, the micro:bit simulator will show two micro:bits -* In the simulator, a radio transmission icon will appear in the top right corner of the micro:bit. The icon will light up as the micro:bit is transmitting data. -* In the simulator, all the code in the coding workspace runs on both virtual micro:bits. You should include for how to send data as well as what to do when it receives data. +We will be playing a form of this "Marco Polo" game using the radio on the micro:bits. -## Marco Polo -Send and receive strings between micro:bits. -On button A pressed, we will send the string Marco and on button B pressed we will send the string Polo. +This activity focuses on using Radio blocks to send and receive strings between micro:bits. -* When communicating between micro:bits, it is important that the micro:bits involved are all using the same group ID. So, the first thing we will do is set the group ID number. -* From the Radio menu, drag a 'radio set group' block to the coding workspace and place the block into the on start block. -* In the 'radio set group block', leave the default value of 1 for the group ID +## Set Group ID Number + +When communicating between micro:bits, it is important that the micro:bits involved are all using the same group ID. So, the first thing we will do is set the group ID number. + +* In Microsoft MakeCode, start a new project and name it: **Marco Polo**. Either delete the 'forever' block in the coding Workspace or move it to the side, as it's not used in the activity. +* Then from the Radio Toolbox, drag a **'radio set group'** block to the coding Workspace and connect into the 'on start' block. In the **'radio set group'** block, leave the default value of 1 for the group ID. ```blocks radio.setGroup(1) ``` -* Drag 2 'on button pressed' blocks to the coding workspace -* Leave one with the default value A and change the other button to B -* From the Radio Toolbox drawer, drag 2 'radio send string' blocks to the coding workspace -* Place one 'radio send string' block into the 'on button A pressed' block, and the other'radio send string' block into the 'on button B pressed' block -* In the 'on button A pressed' block, change the default empty string value of the 'radio send string' block to the string "Marco" -* In the 'on button B pressed' block, change the default empty string value of the 'radio send string' block to the string "Polo" +## Code radio send for button A and button B + +* Drag two **'on button pressed'** blocks to the coding Workspace. Leave one with the default value A , and use the dropdown menu to change the other button to B. +* From the Radio Toolbox drawer, drag two **'radio send string'** blocks to the coding Workspace. Place one **'radio send string'** block into the **'on button A pressed'** block, and the other **'radio send string'** block into the 'on button B pressed' block. Then: +>* In the **'on button A pressed'** block, change the default empty string value of the **'radio send string'** block by typing the string: Marco +>* In the **'on button B pressed'** block, change the default empty string value of the **'radio send string'** block by typing the string: Polo ```blocks input.onButtonPressed(Button.A, () => { @@ -36,9 +34,14 @@ input.onButtonPressed(Button.B, () => { radio.sendString("Polo") }) ``` -* To display the data sent between the micro:bits, drag an 'on radio received receivedString' block to the coding workspace -* From the Basic Toolbox drawer, drag a 'show string' block into the 'on radio received receivedString' block -* From the 'on radio received receivedString' block, drag the 'receivedString' variable block into the default string value of "Hello" in the 'show string' block + +## Code radio received + +* To display the data sent between the micro:bits, drag an **'on radio received (receivedString)'** block to the coding Workspace +**Note:** There are a lot of blocks in the Radio category that look similar. Make sure you use the **'on radio received'** block with **'receivedString'** value. +* From the Basic Toolbox drawer, drag a **'show string'** block into the **'on radio received (receivedString)'** block. Then from the Variables Toolbox drawer, drag a **'receivedString'** variable block to replace the default string value of "Hello" in the **'show string'** block. + +## Complete program Here is the complete Marco Polo program: @@ -55,9 +58,12 @@ input.onButtonPressed(Button.B, () => { radio.setGroup(1) ``` -## Mods +Solution link: [Marco Polo](https://makecode.microbit.org/_5gs2WR1fM8uy) + +## Mod this! + * Add a 'show leds' block to the 'on start' block. We created an image of the initials MP. -* From the Music Toolbox drawer, drag 2 'play tone' blocks to the coding workspace. See [hack your headphones](/projects/hack-your-headphones) for how to connect a speaker or headphones to the micro:bit. +* From the Music Toolbox drawer, drag 2 'play tone' blocks to the coding workspace. * Drag one of the 'play tone' blocks to the 'on button A pressed' block, and the other one to the 'on button B pressed' block. * Change the default value in the 'play tone' block that is inside the 'on button A pressed' block to the value Low C. @@ -85,29 +91,37 @@ basic.showLeds(` `) ``` -## Morse Code +Solution link: [Marco Polo With Mod](https://makecode.microbit.org/_7VrHLecATUzR) + +# Coding activity 2: Morse Code -Send and receive numbers between micro:bits. -Depending on the button pressed, send a different number value between micro:bits. On receiving a number, display a different image unique to the number sent. One number will represent a dot, another a dash and another a space or stop. +Morse code is a character encoding scheme used in telecommunication that encodes text characters as standardized sequences of two different signal durations called dots and dashes. Morse code is named for Samuel F. B. Morse, an inventor of the telegraph. The first versions were invented in the early 1800s and has been refined since then. In the late 1800s, it was used for early radio communication before it was possible to transmit voice. ![Morse code alphabet](/static/courses/csintro/radio/morse.png) -* Set the group ID number. -* Add a 'show string' block to the 'on start' block, to identify the program. -* We choose to change the default string value of "Hello" to the value "Morse Code" +This activity focuses on using Radio blocks to send and receive numbers between micro:bits: + +* Depending on the button pressed, a different number value is sent between micro:bits. +* On receiving a number, the micro:bit will display a different image unique to the number sent. +* One number will represent a dot, another a dash, and another a space or stop. + +## Set the group ID + +* In Microsoft MakeCode, start a new project and name it: **Morse code**. Either delete the 'forever' block in the coding Workspace or move it to the side, as it's not used in the activity. +* Set the Radio group ID number, following the same steps as the previous activity. Then add a **'show string'** block to the **'on start'** block to identify the program. In this example, the default string value of **Hello** is changed to the value **Morse Code**. ```blocks radio.setGroup(1) basic.showString("Morse Code") ``` -* Drag 3 'on button pressed' blocks to the coding workspace. -* Leave one with the default value A, change the value in the second block to B, and change the value in the third block to A+B. -* From the Radio Toolbox drawer, drag 3 'radio send number' blocks to the coding workspace. -* Place one radio send number block into each of the 'on button pressed' blocks. -* In the 'on button A pressed' block, leave the default number value of the 'radio send number' block as 0. -* In the 'on button B pressed' block, change the default number value of the 'radio send number' block to the value 1. -* In the 'on button A+B pressed' block, change the default number value of the 'radio send number' block to the value 2. +## Code the buttons + +* Drag three **'on button pressed'** blocks to the coding workspace. Leave one with the default value A, change the value in the second block to **B**, and change the value in the third block to **A+B**. +* From the Radio Toolbox drawer, drag three **'radio send number'** blocks to the coding workspace and place one **'radio send number'** block into each of the **'on button pressed'** blocks. +>* In the **'on button A pressed'** block, leave the default number value of the **'radio send number'** block as 0. +>* In the **'on button B pressed'** block, change the default number value of the **'radio send number'** block to the value 1. +>* In the **'on button A+B pressed'** block, change the default number value of the **'radio send number'** block to the value 2. ```blocks input.onButtonPressed(Button.A, () => { @@ -121,16 +135,18 @@ input.onButtonPressed(Button.AB, () => { }) ``` -* From the Radio Toolbox drawer, drag an 'on radio received receivedNumber' event handler to the coding workspace. -* Since we will display a different image depending on the number value received, we need a logic block. -* From the Logic Toolbox drawer, drag an 'if...then' block to the coding workspace and place it in the 'on radio received receivedNumber' event handler. +## Code radio received + +* From the Radio Toolbox drawer, drag an 'on radio received (receivedNumber)' event handler to the coding Workspace. + +**Note:** There are a lot of blocks in the Radio category that look similar. Make sure you use the block with the **'receivedNumber'** value. + +* Since we will display a different image depending on the number value received, we need a logic block. From the Logic Toolbox drawer, drag an 'ifâ€Ļthenâ€Ļelse' block to the coding Workspace and place it in the 'on radio received (receivedNumber)' event handler. In order to know whether to display a dot, a dash, or a space/stop image, we need to compare the number received to the values 0, 1, and 2. -* From the Logic Toolbox drawer, drag a 0=0 comparison block into the coding workspace. -* Replace the default value 'true' of the 'if...then' block with the comparison block. -* From the 'on radio received receivedNumber' block, pull down the 'receivedNumber' variable block and drop it into the first slot of the comparison block -* Leave the righthand side default value of zero in the 0=0 block. +* From the Logic Toolbox drawer, drag a **'0=0'** comparison hexagon block onto the coding Workspace and drop it into the **'ifâ€Ļthen'** block replacing the default value of **"true"**. +* From the Variables Toolbox drawer, drag a **'receivedNumber'** variable block onto the coding Workspace and drop it into the first slot of the equals comparison block. Leave the default value of 0 in the second slot. ```blocks radio.onReceivedNumber(function (receivedNumber) { @@ -140,8 +156,7 @@ radio.onReceivedNumber(function (receivedNumber) { }) ``` -* Place a 'show leds' block in the space after the then of the 'if...then' block. -* Create an image to represent a dot. +* From the Basic Toolbox, drag a 'show leds' block to the coding Workspace and drop it under the 'ifâ€Ļthen' clause. Then create an image to represent a dot. ```blocks radio.onReceivedNumber(function (receivedNumber) { @@ -158,13 +173,12 @@ radio.onReceivedNumber(function (receivedNumber) { ``` ### Try it! -* Download your program to the micro:bit -* Press button A on the sending micro:bit -* Does this cause a dot to be displayed on the receiving micro:bit? -* However, pressing button A again does not appear to send another dot as the image on the receiving micro:bit does not appear to change. -Challenge question: How can we fix this? -* Add a 'pause' block and a 'clear screen' block after the 'show leds' block +Download the program to the micro:bit and press button A on the sending micro:bit. Does this cause a dot to be displayed on the receiving micro:bit? + +However, pressing button A again does not appear to send another dot as the image on the receiving micro:bit does not appear to change. + +**Challenge question:** How can we fix this? **Answer:** Add a 'pause' block and a 'clear screen' block after the 'show leds' block. ```blocks radio.onReceivedNumber(function (receivedNumber) { @@ -181,19 +195,25 @@ radio.onReceivedNumber(function (receivedNumber) { } }) ``` -Try running the program again. -Now each time the sender presses button A, you see a dot appear. + +Try running the program again. +Now, each time the sender presses button A, you see a dot appear. ![micro:bit dot display](/static/courses/csintro/radio/microbit-dot-display.png) -* You can now right-click on the 'ifâ€Ļthen' block and select Duplicate to copy that piece of code twice for the other 2 values that a sender may send. +## Code the other received images -![If-block, right-click and duplicate](/static/courses/csintro/radio/if-then-duplicate.png) +Now we need to specify what to display if we receive a 1 or 2. -* Change the values on the righthand side of the comparison block to 1, and 2. -* Modify the images displayed to show a dash, and a full screen of lights +* In the **'ifâ€Ļthenâ€Ļelse'** block, select the plus (+) icon to create an **'else if'** clause. +* Right-click on the equals comparison block in the 'if' clause and select Duplicate to create a copy. +* Then, drag this new equals comparison block into the 'else if' clause. +* Change the value in the second slot of the equals comparison block from 0 to 1. We don't have to test **'receivedNumber'** value in the third **'else'** clause—if the value is not 0 or 1, then it must be 2, since there are only three possibilities. +* From the Basic Toolbox drawer, drag **'show leds'**, **'pause'**, and **'clear screen'** blocks to the **'else if'** and **'else'** clauses. Then, modify the **'show leds'** images displayed: +>* For the **'else if (receivedNumber=1)'**, show a dash. +>* For the **'else'** clause (which is when the **'receivedNumber'** variable equals 2), show a full screen of lights. -### Morse code program +## Complete program ```blocks @@ -245,31 +265,25 @@ radio.setGroup(1) basic.showString("Morse Code") ``` -### Try it! -* Download your program to the micro:bit -* Press buttons A, B, and A+B together on the micro:bit +Solution link: [Morse Code](https://makecode.microbit.org/_846Kyk4619yh) + +## Try it! -Challenge question: Can our code be made more efficient? -* Whenever you look over a program and see the same lines of code repeated, there is usually a chance to improve the code making it more efficient by reducing the number of lines of code -* What lines are repeated in our program? If...then, pause, clear screen -* Can we edit the code to use only one 'if...then' block, one 'pause' block, and one 'clear screen' block? Yes! +Download your program to the micro:bit. Press buttons A, B, and A+B together on the sending micro:bit to see the associated image on the receiving micro:bit. -## Making our code more efficient +## Mod this! -Remind students that they can edit the 'if...then' block, adding as many 'else if' conditions as needed. -They can do this by clicking on the **(+)** or **(-)** symbols on the 'if...then' block. +**A final else** -![Add else-if to if-then block](/static/courses/csintro/radio/if-then-else-if.png) +In a conditional that might receive a number of different values, it is good coding practice to have a catch-all 'else' clause. In the example, if any number value other than the ones we coded for (0,1, and 2) is received, we can signal the user that an error has occurred by using a 'show icon' block to display an X. -A final else -In a conditional that might receive a number of different values, it is good coding practice to have a catch-all 'else' clause. In our example, if any number value other than the ones we coded for (0,1, and 2) is received, we can signal the user that an error has occurred by using a 'show icon' block to display an X. +**The pause and clear screen** -The pause and clear screen -Rather than repeat these lines of code 3 times, we can move the 'pause' block and the 'clear screen' block outside of the edited 'if...thenâ€Ļelse' block. +* Rather than repeat these lines of code three times, we can move the **'pause'** block and the **'clear screen'** block outside of the edited **'ifâ€Ļthenâ€Ļelse'** block and inside the **'on radio received (receivedNumber)'** block. -Now our program runs as we designed it to run and is more efficient, too! +Now our program runs as we designed it to run and is more efficient, too! Download the revised program to the micro:bits and test it out. -Final Morse Code Program: +### Complete program with mod ```blocks input.onButtonPressed(Button.A, () => { @@ -316,6 +330,24 @@ radio.setGroup(1) basic.showString("Morse Code") ``` +Solution link: [Morse Code With Mod](https://makecode.microbit.org/_fWpDXK1hFFC9) + +## Knowledge Check + +**Questions:** + +1. Using the radio blocks, what information can you send to a micro:bit? +2. Why did we all have to set our 'radio set group' block to a default value of 1? +3. Why was it important to set a final catch-all 'else' clause in the conditional you used for the Morse code activity? +4. When editing code, why do we look for lines of code that repeat? + +**Answers:** + +1. You can send a number, a string, or a string/number combination. You can also give a micro:bit instructions on what to do when it receives a radio message. +2. So that the micro:bits would all be using the same group ID number and could send and receive messages. +3. So that it would display an error message if it received a number value beyond 0, 1, or 2. +4. To make code more efficient and to reduce the number of lines of code needed. + ```package radio ``` \ No newline at end of file diff --git a/docs/courses/csintro/radio/overview.md b/docs/courses/csintro/radio/overview.md index 7689ed8b906..bd1a527857c 100644 --- a/docs/courses/csintro/radio/overview.md +++ b/docs/courses/csintro/radio/overview.md @@ -1,19 +1,15 @@ # Introduction -Up to this point, we have been primarily challenging students to collaborate while they create their own projects. This lesson, on communication using the micro:bit radio, is a great opportunity to have students work in pairs on a project. Have kids find a partner to work with for this lesson, and make sure they are seated next to each other. -  -Note: Many teachers find the concept of “pair programming” to be a valuable way to have students collaborate when programming. Two students share one computer, with one student at the keyboard acting as the driver, and the other student providing directions as the navigator. Students must practice good communication with each other throughout the entire programming process. - The micro:bit allows you to communicate with other micro:bits in the area using the blocks in the Radio category. You can send a number, a string (a word or series of characters) or a string/number combination in a radio packet. You can also give a micro:bit instructions on what to do when it receives a radio packet. -## ~ hint +### ~ hint -Watch this video to see how the radio hardware works on the @boardname@: +#### Bonus -https://www.youtube.com/watch?v=Re3H2ISfQE8 +Watch this video to see how the radio hardware works on the micro:bit: -## ~ +https://www.youtube.com/watch?v=Re3H2ISfQE8 -This lesson starts with a “plugged” unplugged activity, in which students use their micro:bits to explore an advanced simulation. The code is quite complex, so students will focus more on how to use the micro:bits to explore aspects of viruses and epidemics, than the intricacies of the code itself. +### ~   -The project for this lesson will challenge students to work together to send and receive some sort of data to and from each other. There is a wide range of simple and complex projects kids can try, but whatever they choose it is a whole lot of fun to communicate with each other using the micro:bits! +The project for this lesson will challenge you to send and receive some sort of data to and from a pair of micro:bits. There is a wide range of simple and complex projects you can try, but whatever you choose, it is a whole lot of fun to communicate using the micro:bits! \ No newline at end of file diff --git a/docs/courses/csintro/radio/project.md b/docs/courses/csintro/radio/project.md index 35832342369..4335297936c 100644 --- a/docs/courses/csintro/radio/project.md +++ b/docs/courses/csintro/radio/project.md @@ -1,8 +1,6 @@ # Project: Radio project -For this project, students should work in pairs to design a project that incorporates radio communication to send and receive data in some way. Some projects may have two separate programs: One that receives data, and one that sends data. Students might each choose to submit one program in that case. - -In other cases, a pair of students might submit one program that has both sending and receiving code in it, and the same code is uploaded to two or more micro:bits. +For this project, you'll be coding two micro:bits and making use of the Radio blocks to send and receive data between them. Some projects may even have two separate programs: One that receives data, and one that sends data. ## Project Ideas @@ -16,20 +14,22 @@ Create a piece of interactive artwork that receives something as input over the This is a simple three-note keyboard that uses wooden paint stirrers and copper tape to make a connection to each of the three pins on the micro:bit. ![Keyboard with copper tape](/static/courses/csintro/radio/keyboard-copper-tape.png) -Keyboard with copper tape connections +_Keyboard with copper tape connections_   -When a key is pressed, it sends a number over the radio to a second micro:bit that plays the appropriate tone over a set of earbuds. This allows you to use each of the three pins on the first micro:bit to play a different tone. +When a key is pressed, it sends a number over the radio to a second micro:bit that plays the appropriate tone. This allows you to use each of the three pins on the first micro:bit to play a different tone. ![Second micro:bit that plays notes](/static/courses/csintro/radio/microbit-number-two.png) -Second micro:bit that plays the notes +_Second micro:bit that plays the notes_ + +### ~ hint -#### ~ hint +#### Bonus -This project uses touch pin inputs. See how the @boardname@ detects a press at a pin or on something connected to a pin in this video: +This project uses touch pin inputs. See how the micro:bit detects a press at a pin or on something connected to a pin in this video: https://www.youtube.com/watch?v=GEpZrvbsO7o -#### ~ +### ~ #### 3-Note keyboard program @@ -96,49 +96,23 @@ basic.showLeds(` basic.clearScreen() ``` +Solution link: [makecode.microbit.org/_iXKbWu8f2H60]() + ### Radio tennis In this project, the tennis racquets alternate displaying a ball on the micro:bit LED screen. When you swing the racquet, the ball disappears from one micro:bit display and shows up on the other micro:bit's display. ![Radio tennis racquets](/static/courses/csintro/radio/radio-tennis-racquets.jpg) -Radio Tennis racquets (made from cardboard) +_Radio Tennis racquets (made from cardboard)_ ## Reflection -Have students write a reflection of about 150–300 words, addressing the following points: -* What kind of Project did you do? How did you decide what to pick? +Write a short reflection of about 150–300 words, addressing the following points: + +* What kind of project did you do? How did you decide what to pick? * How does your project use radio communication? -* Are there separate programs for the Sender and the Receiver micro:bits? Or 1 program for both? +* Are there separate programs for the Sender and the Receiver micro:bits? Or one program for both? * Describe something in your project that you are proud of. -* Describe a difficult point in the process of designing this program, and explain how you resolved it. -* What feedback did your beta testers give you? How did that help you improve your design? -  -## Assessment - -**Competency scores**: 4, 3, 2, 1 -  -### Radio - -**4 =** Effectively uses the Radio to send and receive data, with meaningful actions and responses for each.
-**3 =** Effectively uses the Radio to send or receive data, with meaningful actions and responses for each.
-**2 =** Use of Radio is incomplete or non-functional and/or tangential to operation of program.
-**1 =** No working and/or meaningful use of Radio. -     -### micro:bit program -**4 =** micro:bit program:
-`*` Uses Radio blocks in a way that is integral to the program
-`*` Compiles and runs as intended
-`*` Meaningful comments in code
-**3 =** micro:bit program lacks 1 of the required elements.
-**2 =** micro:bit program lacks 2 of the required elements.
-**1 =** micro:bit program lacks all of the required elements. - -### Collaboration reflection - -**4 =** Reflection piece addresses all prompts.
-**3 =** Reflection piece lacks 1 of the required elements.
-**2 =** Reflection piece lacks 2 of the required elements.
-**1 =** Reflection piece lacks 3 of the required elements.   - -```package -radio -``` \ No newline at end of file +* Describe a difficult point in the process of designing this program and explain how you resolved it. +* What feedback did your testers give you? How did that help you improve your design? +* How would you improve your project, given more time? +* Publish your MakeCode program and include the link. \ No newline at end of file diff --git a/docs/courses/csintro/variables.md b/docs/courses/csintro/variables.md index a170272950b..f1156169248 100644 --- a/docs/courses/csintro/variables.md +++ b/docs/courses/csintro/variables.md @@ -2,11 +2,11 @@ ![Variable value](/static/courses/csintro/variables/cover.jpg) -This lesson introduces the use of variables to store data or the results of mathematical operations. Students will practice giving variables unique and meaningful names. We will also introduce the basic mathematical operations for adding, subtracting, multiplying, and dividing variables. +This unit introduces the use of variables to store information. You will practice giving variables unique and meaningful names, and use basic mathematical operations for adding, subtracting, multiplying, and dividing variable values. You'll code a program for the micro:bit that keeps and displays the score of a game of *Rock, Paper Scissors* by using the programmable buttons for input and the LED screen for output. In the final project, you'll code your own unique program using variables, and design and build an object that uses the micro:bit to track score, count steps, turns, or something else. ## Lesson Objectives -Students will... +You will... * Understand what variables are and why and when to use them in a program. * Learn how to create a variable, set the variable to an initial value, and change the value of the variable within a micro:bit program. @@ -19,14 +19,9 @@ Students will... ## Lesson plan 1. [**Overview**: Variables in Daily Life](/courses/csintro/variables/overview) -2. [**Unplugged**: Rock Paper Scissors](/courses/csintro/variables/unplugged) -3. [**Activity**: Make a Game Scorekeeper](/courses/csintro/variables/activity) -4. [**Project**: Everything Counts](/courses/csintro/variables/project) - -## Flipgrid - -The [Flipgrid](https://info.flipgrid.com/) topic for the **Variables** lesson: https://flipgrid.com/dc42bdcc +2. [**Activity**: Make a Game Scorekeeper](/courses/csintro/variables/activity) +3. [**Project**: Everything Counts](/courses/csintro/variables/project) ## Related standards -[Targeted CSTA standards](/courses/csintro/variables/standards) +[Targeted CSTA standards](/courses/csintro/variables/standards) \ No newline at end of file diff --git a/docs/courses/csintro/variables/activity.md b/docs/courses/csintro/variables/activity.md index 1c74b05b75d..4241199e092 100644 --- a/docs/courses/csintro/variables/activity.md +++ b/docs/courses/csintro/variables/activity.md @@ -1,17 +1,14 @@ # Activity: Scorekeeper -This micro:bit activity guides the students to create a program with three variables that will keep score for their _Rock Paper Scissors_ game. +This micro:bit activity guides you to create a program with three variables that will keep score for a game of _Rock Paper Scissors_. -Tell the students that they will be creating a program that will act as a scorekeeper for their next Rock Paper Scissors game. They will need to create variables for the parts of scorekeeping that change over the course of a gaming session. What are those variables? +To do this, you will need to create variables for the parts of scorekeeping that change over the course of a gaming session. What are those variables? * The number of times the first player wins * The number of times the second player wins * the number of times the players tie -Creating and naming variables: Lead the students to create meaningful names for their variables. -* What would be a unique and clear name for the variable that will keep track of the number of times Player A wins? -* Student suggestions may be: ``PAW``, ``PlayerA``, ``AButtonPress``, ``AButtonCount``, ``PlayerAWins``... -* Discuss why (or why not) different suggestions make clear what value the variable will hold. In general, variable names should clearly describe what type of information they hold. +First, let's consider the names of our variables. In general, variable names should clearly describe what type of information they hold. They should be clear and easy for a reader to understand regardless of their familiarity with your program. In MakeCode, from the Variables menu, make and name these three variables: `PlayerAWins`, `PlayerBWins`, `PlayersTie`. @@ -33,22 +30,24 @@ let PlayersTie = 0 In our program, we want to keep track of the number of times each player wins and the number of times they tie. We can use the buttons A and B to do this. Pseudocode: + * Press button A to record a win for player A * Press button B to record a win for player B * Press both button A and button B together to record a tie We already initialized these variables and now need to code to update the values at each round of the game. + * Each time the scorekeeper presses button A to record a win for Player A, we want to add 1 to the current value of the variable `PlayerAWins`. * Each time the scorekeeper presses button B, to record a win for Player B, we want to add 1 to the current value of the variable `PlayerBWins`. * Each time the scorekeeper presses both button A and button B at the same time to record a tie, we want to add 1 to the current value of the variable `PlayersTie`. -From the Input menu, drag 3 of the ‘on button A pressed’ event handlers to your Programming Workspace. +From the Input menu, drag 3 of the 'on button A pressed' event handlers to your Programming Workspace. ![onButtonPressed A](/static/courses/csintro/variables/on-button-pressed.png) -Leave one block with ‘A’. Use the drop-down menu in the block to choose ‘B’ for the second block and ‘A+B’ for the third block. +Leave one block with 'A'. Use the drop-down menu in the block to choose 'B' for the second block and 'A+B' for the third block. -From the Variables menu, drag 3 of the ‘change PlayersTie by 1’ blocks to your Programming Workspace. +From the Variables menu, drag 3 of the 'change PlayersTie by 1' blocks to your Programming Workspace. ![Change variable](/static/courses/csintro/variables/change-variable.png) @@ -74,15 +73,15 @@ input.onButtonPressed(Button.AB, () => { ## User feedback Whenever the scorekeeper presses button A, button B, or both buttons together, we will give the user visual feedback acknowledging that the user pressed a button. We can do this by coding our program to display: -* an ‘A’ each time the user presses button A to record a win for Player A, -* a ‘B’ for each time the user presses button ‘B’ to record a win for Player B, -* a ‘T’ for each time the user presses both button A and button B together to record a tie. +* an 'A' each time the user presses button A to record a win for Player A, +* a 'B' for each time the user presses button 'B' to record a win for Player B, +* a 'T' for each time the user presses both button A and button B together to record a tie. -We can display an ‘A’, ‘B’, or ‘T’ using either the ‘show leds’ block or the ‘show string’ block. +We can display an 'A', 'B', or 'T' using either the 'show leds' block or the 'show string' block. ![Show LEDs](/static/courses/csintro/variables/show-leds.png) -In this example, we have used the ‘show leds’ block. +In this example, we have used the 'show leds' block. ```blocks let PlayerAWins = 0 @@ -123,14 +122,14 @@ input.onButtonPressed(Button.AB, () => { basic.clearScreen() }) ``` -Notice that we added a ‘clear screen’ block after showing ‘A’, ‘B’, or ‘T’. +Notice that we added a 'clear screen' block after showing 'A', 'B', or 'T'. What do you think would happen if we did not clear the screen? Try it. ## Showing the final values of the variables To finish our program, we can add code that tells the micro:bit to display the final values of our variables. -Since we have already used buttons A and B, we can use the ‘on shake’ event handler block to trigger this event. -We can use the ‘show string’, ‘show leds’, ‘pause’, and ‘show number’ blocks to display these final values in a clear way. +Since we have already used buttons A and B, we can use the 'on shake' event handler block to trigger this event. +We can use the 'show string', 'show leds', 'pause', and 'show number' blocks to display these final values in a clear way. Here is the complete program. ```blocks @@ -202,26 +201,28 @@ PlayersTie = 0 ### ~ hint -Buttons have been used as human input devices since computers first existed. Watch this video and see how they let tell the @boardname@ to do something. +#### Buttons for input + +Buttons have been used as human input devices since computers first existed. Watch this video and see how they let the user tell the micro:bit to do something. https://www.youtube.com/watch?v=t_Qujjd_38o ### ~ ## Try it out! -Download the Scorekeeper program to the micro:bit, and have the students play one last round of Rock Paper Scissors using their micro:bits to act as the Scorekeeper! +Download the Scorekeeper program to the micro:bit, and find someone to play *Rock, Paper, Scissors* with you using your micro:bit to act as the Scorekeeper! -## ‘Adding’ on with mathematical operations +## 'Adding' on with mathematical operations There is more we can do with the input we received using this program. We can use mathematical operations on our variables. -Example: Perhaps you’d like to keep track of, and show the player the total number of ‘rounds’ that were played. To do this, we can add the values stored in the variables we created to keep track of how many times each player won and how many times they tied. +Example: Perhaps you'd like to keep track of, and show the player the total number of 'rounds' that were played. To do this, we can add the values stored in the variables we created to keep track of how many times each player won and how many times they tied. In order to do this, we can add the code to our program under the 'on shake' event handler. * First, display a string to show the player that the following sum represents the total number of rounds played. * Our program will add the values stored in the variables `PlayerAWins`, `PlayerBWins`, and `PlayersTie` and then display the sum of this mathematical operation. * The blocks for the mathematical operations adding, subtracting, multiplying, and dividing are listed in the Math section of the Toolbox. ->**Note:** Even though there are 4 blocks shown for these 4 operations, you can access any of the four operations from any of the four blocks, and you can also access the exponent operation from these blocks. +**Note:** Even though there are four blocks shown for these four operations, you can access any of the four operations from any of the four blocks, and you can also access the exponent operation from these blocks. ![Adding block](/static/courses/csintro/variables/adding-block.png) @@ -247,6 +248,20 @@ Remember that the micro:bit is a device that processes input and displays it as What other math operations could provide valuable information from the values stored in these variables? Examples: -* Calculate and display a player’s wins and/or losses as a percentage of all rounds played. + +* Calculate and display a player's wins and/or losses as a percentage of all rounds played. * Calculate a display the number of tied games as a percentage of all rounds played. +## Knowledge Check + +Questions: + +1. What's the difference between a constant and a variable? +2. Why is it important to name variables in a clear and meaningful way? +3. **True or false:** You can only use the default variable names provided in the Variables toolbox drawer. + +Answers: + +1. A constant has a value that doesn't change. A variable has a value that may change. +2. Variable names should clearly describe what type of information they hold so they are easily recognizable in the program and you can find problems or bugs easier. +3. **False.** You can make a variable with any name you want/need for a program with the Make a Variable button. \ No newline at end of file diff --git a/docs/courses/csintro/variables/overview.md b/docs/courses/csintro/variables/overview.md index 40ac240b959..919390a0a65 100644 --- a/docs/courses/csintro/variables/overview.md +++ b/docs/courses/csintro/variables/overview.md @@ -1,23 +1,23 @@ # Introduction -Computer programs process information. Some of the information that is input, stored, and used in a computer program has a value that is **constant**, meaning it does not change throughout the course of the program. An example of a **constant** in math is ‘pi’ because ‘pi’ has one value that never changes. Other pieces of information have values that **vary** or change during the running of a program. Programmers create **variables** to hold the value of information that may change. In a game program, a variable may be created to hold the player’s current score, since that value would change (hopefully!) during the course of the game. +From our previous lesson, we learned that computer programs process information. Some of the information that is input, stored, and used in a computer program has a value that is **constant**, meaning it does not change throughout the course of the program. An example of a constant in math is “pi” because “pi” has one value that never changes (it is 3.14). Other pieces of information have values that vary or change during the running of a program. Programmers create **variables** to hold the value of information that may change. In a game program, a variable may be created to hold the player’s current score, since that value would change (hopefully!) during the course of the game. -Ask the students to think of some pieces of information in their daily life that are **constants** and others that are **variables**. +Try and think of some pieces of information from your daily life that are constants and others that are variables. -* What pieces of information have values that don’t change during the course of a single day (constants)? -* What pieces of information have values that do change during the course of a single day (variables) -Constants and variables can be numbers and/or text. +* What pieces of information have values that don’t change during the course of a single day (i.e., are constants)? +* What pieces of information have values that do change during the course of a single day (i.e., are variables)? ## Examples In one school day... -* Constants: The day of the week, the year, student’s name, the school’s address -* Variables: The temperature/weather, the current time, the current class, whether they are standing or sitting... +* Constants: The day of the week, the year, your name, your school’s address, your birthday +* Variables: The temperature/weather, the current time, the current class, whether you are standing or sittingâ€Ļ -Variables hold a specific type of information. The micro:bit's variables can keep track of numbers, strings, booleans, and sprites. The first time you use a variable, its type is assigned to match whatever it is holding. From that point forward, you can only change the value of that variable to another value of that same type. +Variables hold a specific type of information. The micro:bit’s variables can keep track of **numbers, strings, Booleans, sprites,** and **arrays**. The first time you use a variable, its type is assigned to match whatever it is holding. From that point forward, you can only change the value of that variable to another value of that same type. -* A number variable could hold numerical data such as the year, the temperature, or the degree of acceleration. -* A string variable holds a string of alphanumeric characters such as a person's name, a password, or the day of the week. -* A boolean variable has only two values: true or false. You might have certain things that happen only when the variable called _gameOver_ is false, for example. -* A sprite is a special variable that represents a single dot on the screen and holds two separate values for the row and column the dot is currently in. +* A **number** variable could hold numerical data such as the year, the temperature, or the degree of acceleration. +* A **string** variable holds a string of alphanumeric characters such as a person’s name, a password, or the day of the week. +* A **Boolean** variable has only two values: true or false. For example, you might have certain things that happen only when the variable called GameOver is false. +* A **sprite** is a special variable that represents a single dot on the screen and holds two separate values for the row and column the dot is currently in. +* An **array** is another special type of variable that holds a list of multiple items. \ No newline at end of file diff --git a/docs/courses/csintro/variables/project.md b/docs/courses/csintro/variables/project.md index ebb9425aa35..f06567ddc96 100644 --- a/docs/courses/csintro/variables/project.md +++ b/docs/courses/csintro/variables/project.md @@ -1,14 +1,27 @@ # Project: Everything counts -This is an assignment for students to come up with a micro:bit program that counts something. -Their program should keep track of **input** by storing values in variables, and provide **output** in some visual and useful way. -Students should also perform mathematical operations on the variables to give useful output. +In this assignment, you'll come up with a micro:bit program that counts something. Your program should keep track of input by storing values in variables and provide output in some visual and useful way. You should also perform mathematical operations on the variables to give useful output. ## Input -Remind the students of all the different inputs available to them through the micro:bit. +Review all the different inputs available to you through the micro:bit. ![micro:bit input list](/static/courses/csintro/variables/input-list.png) +* Acceleration +* Light level +* Button is pressed +* Compass heading +* Temperature +* Running time +* On shake +* On button pressed +* On logo down +* On logo up +* One pin pressed +* On screen down +* On screen up +* Pin is pressed + ## Project Ideas ### Duct tape wallet @@ -17,9 +30,9 @@ You can see the instructions for creating a durable, fashionable wallet or purse **Extra mod:** Use other inputs to handle cents, and provide a way to display how much money is in the wallet in dollars and cents. -### Umpire’s baseball counter (pitches and strikes) +### Umpire’s baseball counter (balls and strikes) -In baseball during an at-bat, umpires must keep track of how many pitches have been thrown to each batter. Use Button A to record the number of balls (up to 4) and the number of strikes (up to 3). +During an at-bat, baseball umpires must keep track of what type of pitches have been thrown to each batter. Use Button A to record the number of balls (up to 4) and the number of strikes (up to 3). **Extra mod:** Create a way to reset both variables to zero, create a way to see the number of balls and strikes on the screen at the same time. @@ -42,13 +55,13 @@ Create an adding machine. Use Button A to increment the first number, and Button **Extra mod:** Find a way to select and perform other math operations. ![micro:bit top and spin counter](/static/courses/csintro/variables/microbit-spinner.png) -Homemade top with micro:bit revolution counter +_Homemade top with micro:bit revolution counter_ ![Duct tape wallet](/static/courses/csintro/variables/duct-tape-wallet.jpg) -Duct tape wallet with micro:bit display +_Duct tape wallet with micro:bit display_ ![Baseball pitch counter](/static/courses/csintro/variables/baseball-counter.jpg) -Baseball pitch counter +_Baseball pitch counter_ ## Process @@ -75,7 +88,7 @@ basic.forever(() => { ## Reflection -Have students write a reflection of about 150–300 words, addressing the following points: +Write a short reflection (150–300 words) about your project, addressing the following points: * What was the problem you were trying to solve with this project? * What were the Variables that you used to keep track of information? @@ -85,44 +98,4 @@ Have students write a reflection of about 150–300 words, addressing the follow * What was something that was surprising to you about the process of creating this project? * Describe a difficult point in the process of designing this project, and explain how you resolved it. -## Assessment - -**Competency scores**: 4, 3, 2, 1 - -### Variables ->**4 =** At least 3 different variables are implemented in a meaningful way.
-**3 =** At least 2 variables are implemented in a meaningful way.
-**2 =** At least 1 variable is implemented in a meaningful way.
-**1 =** No variables are implemented. - -### Variable names - ->**4 =** All variable names are unique and clearly describe what information values the variables hold.
-**3 =** The majority of variable names are unique and clearly describe what information values the variables hold.
-**2 =** A minority of variable names are unique and clearly describe what information values the variables hold.
-**1 =** None of the variable names clearly describe what information values the variables hold.
- -### Mathematical operations ->**4 =** Uses a mathematical operation on at least two variables in a way that is integral to the program.
-**3 =** Uses a mathematical operation on at least one variable in a way that is integral to the program.
-**2 =** Uses a mathematical operation incorrectly or not in a way that is integral to the program.
-**1 =** No mathematical operations are used. - -### micro:bit program ->**4 =** micro:bit program:
-` *` Uses variables in a way that is integral to the program
-` *` Uses mathematical operations to add, subtract, multiply, and/or divide variables
-` *` Compiles and runs as intended
-` *` Meaningful comments in code
-**3 =** micro:bit program lacks 1 of the required elements.
-**2 =** micro:bit program lacks 2 of the required elements.
-**1 =** micro:bit program lacks 3 or more of the required elements. - -### Collaboration reflection - ->**4 =** Reflection piece addresses all prompts.
-**3 =** Reflection piece lacks 1 of the required elements.
-**2 =** Reflection piece lacks 2 of the required elements.
-**1 =** Reflection piece lacks 3 of the required elements. - diff --git a/docs/courses/csintro/variables/unplugged.md b/docs/courses/csintro/variables/unplugged.md index 36baf3be63a..2517d6b6541 100644 --- a/docs/courses/csintro/variables/unplugged.md +++ b/docs/courses/csintro/variables/unplugged.md @@ -1,28 +1,22 @@ # Unplugged: Keeping score -The objective of this activity is to experience creating and working with variables by pairing up and playing _Rock Paper Scissors_. +The objective of this activity is to experience creating and working with variables by pairing up and playing _Rock Paper Scissors_. ![Rock-paper-scissors hands](/static/courses/csintro/variables/rps-sketch.jpg) -Ask students to keep track of their scores on paper. -You can also have students play in groups of three with the third student acting as the scorekeeper. +Find someone to play Rock, Paper, Scissors with you. On a separate sheet of paper, keep track of how many times each player wins as well as the number of times you end up in a tie. -Students will keep track of how many times each player wins as well as the number of times the players tie. +**Play**: Play Rock Paper Scissors for a few minutes. When done, add up your scores and how many ‘rounds’ you played. -**Play**: Have students play Rock Paper Scissors for about a minute. When done, ask the students to add up their scores and how many ‘rounds’ they played. - -**Play again**: Tell students they will now start over and play again for another minute. When done, ask the students to add up their scores and how many ‘rounds’ they played. - -Ask some students to share how they kept track of player scores. -There may be some variety, but most will have written down the players’ names and then beside or below the names, marks representing the ‘wins’ of each player. And they may have made a separate place for recording ties. +Take a look at how you kept track of your player scores. Usually, scorekeepers will write down the players’ names and then beside or below the names, marks representing the ‘wins’ of each player. You may have made a separate place for recording ties. ![Score sheet](/static/courses/csintro/variables/mary-doug-score.jpg) Sample score-keeping sheet -Ask the students what parts of the score sheet represent **constants**, values that do not change through the course of a gaming session. +Now think about what parts of the score sheet represent **constants**: values that do not change through the course of a gaming session. **Example**: The players’ names are constants. -Ask the students what parts of the score sheet represent **variables**, values that do change through the course of a gaming session. +Consider what parts of the score sheet represent **variables**: values that do change through the course of a gaming session. **Example**: The players’ number of wins are variables. diff --git a/docs/courses/logic-lab.md b/docs/courses/logic-lab.md index 3d90ab0b7c9..8e288a8f6f1 100644 --- a/docs/courses/logic-lab.md +++ b/docs/courses/logic-lab.md @@ -2,7 +2,7 @@ ![Logic lab header image](/static/courses/logic-lab/logic-lab-header.jpg) -A basic aspect of knowledge and understanding is whether something is true or not. Considering conditions around you and making a conclusion about something being true or false means that you are using logic. Computers and, in fact, all of digital electronics rely on this idea of logic to process information and give results in terms of conditions being true or false. Logic is used almost everywhere in the programs you write in places where you want decide to do one task or another. +A basic aspect of knowledge and understanding is whether something is true or not. Considering conditions around you and making a conclusion about something being true or false means that you are using logic. Computers and, in fact, all of digital electronics rely on this idea of logic to process information and give results in terms of conditions being either true or false. Logic is used almost everywhere in the programs you write in the places where you want decide to do one task or another. ## Logic topics @@ -13,3 +13,9 @@ These topic sections teach you about applying logic to conditions and using Bool * [Logic Explorer](/courses/logic-lab/explorer) * [Logic Gates](/courses/logic-lab/logic-gates) * [Programmable Logic](/courses/logic-lab/programmable) + +## ~button /courses/logic-lab/expressions + +Let's get started! + +## ~ \ No newline at end of file diff --git a/docs/courses/logic-lab/elements.md b/docs/courses/logic-lab/elements.md index 9f7780d0e82..746d6d306be 100644 --- a/docs/courses/logic-lab/elements.md +++ b/docs/courses/logic-lab/elements.md @@ -6,7 +6,7 @@ Whether creating equations in Boolean algebra or using them in your programs, yo Boolean (logical) equations are expressed in a way similar to mathmatical equations. Variables in Boolean expressions though, have only two possible values, ``true`` or ``false``. For an equation using a logical expression, the equivalant sides of the equal sign ,``=``, will be only ``true`` or ``false`` too. -The following list shows the basic notation elements for Boolean expressions. +The following list shows the basic notation elements for variables and operators in Boolean expressions: * ``~A``: the inverse (**NOT**) of ``A``, when ``A`` is ``true``, ``~A`` is ``false`` * ``A + B``: the value of ``A`` **OR** ``B`` @@ -205,3 +205,10 @@ F | F | F T | F | T F | T | T T | T | F +
+ +## ~button /courses/logic-lab/explorer + +NEXT: Logic Explorer + +## ~ diff --git a/docs/courses/logic-lab/explorer.md b/docs/courses/logic-lab/explorer.md index fad606507a8..6c59d792ba5 100644 --- a/docs/courses/logic-lab/explorer.md +++ b/docs/courses/logic-lab/explorer.md @@ -4,7 +4,7 @@ As a way to see how the basic logical operators work, we'll make a program to te ## Inputs and output -Make an array called ``||variables:inputs||`` with two values, ``false`` and ``true``, as logical inputs. Add another variable ``||variables:Q||`` to receive the resulting value of a logical expression as output. +Make an ``||arrays:array||`` called ``||variables:inputs||`` with two values, ``false`` and ``true``, as logical inputs. Add another variable ``||variables:Q||`` to receive the resulting value of a logical expression as output. ```blocks let inputs = [false, true] @@ -17,7 +17,7 @@ To start with, we'll make a single input test for the variable ``||variables:A|| 1. Get a ``||loops:for element||`` loop and put it in the ``||loops:on start||``. Rename the ``||variables:index||`` variable to ``||variables:A||`` and switch the ``||variables:list||`` variable to ``||variables:inputs||``. 2. Pull a ``||variables:set Q to||`` block into the ``||loops:for element||`` loop and set the value to ``||logic:false||``. -3. Go find the ``||logic:if then else||`` and put in below the ``||variables:set Q to||``. Pick up a ``||variables:Q||`` in ``||variables:VARIABLE||`` and drop it onto the ``||logic:false||`` to replace it. +3. Go find the ``||logic:if then else||`` and put in below the ``||variables:set Q to||``. Pick up a ``||variables:Q||`` from the ``||variables:VARIABLES||`` Toolbox drawer and drop it onto the ``||logic:false||`` to replace it. 4. Move a ``||basic:show icon||`` inside the ``||logic:if then||`` section and change the image to a ``t-shirt``. This is our image for a ``true`` output. 5. Move a ``||basic:show icon||`` inside the ``||logic:else||`` section and change the image to a ``small diamond``. This is our image for a ``false`` output. 6. Just below the ``||logic:if then else||``, put in a ``||loops:pause||``, a ``||basic:clear screen||``, and another ``||basic:pause||`` block. Set the time for each ``||basic:pause||`` to ``500``. @@ -143,7 +143,7 @@ A | B | A · B ## XOR test -To test XOR, we'll use the XOR expression from [Boolean elements](/courses/logic-lab/elements#xor). Drag and place the ``||logic:LOGIC||`` blocks to make the ``||variables:Q||`` equation to look like this: +To test XOR, we'll use the XOR expression from [Boolean elements](/courses/logic-lab/elements#xor). Drag and place the ``||logic:LOGIC||`` blocks in to make the ``||variables:Q||`` equation to look like this: ```block let A = false @@ -159,4 +159,11 @@ A | B | A ⊕ B **false** | **false** | ``[basic.showIcon(IconNames.SmallDiamond)]`` **false** | **true** | ``[basic.showIcon(IconNames.TShirt)]`` **true** | **false** | ``[basic.showIcon(IconNames.TShirt)]`` -**true** | **true** | ``[basic.showIcon(IconNames.SmallDiamond)]`` \ No newline at end of file +**true** | **true** | ``[basic.showIcon(IconNames.SmallDiamond)]`` +
+ +## ~button /courses/logic-lab/logic-gates + +NEXT: Logic Gates + +## ~ \ No newline at end of file diff --git a/docs/courses/logic-lab/expressions.md b/docs/courses/logic-lab/expressions.md index c4d7ba8efc0..9f025d855bb 100644 --- a/docs/courses/logic-lab/expressions.md +++ b/docs/courses/logic-lab/expressions.md @@ -1,8 +1,8 @@ # Logic and expressions -The use and study of _logic_ involves finding a new fact by analyzing whether some other facts together can prove to be true. Some facts, or conditions, when looked at together may prove another fact to be true, or maybe false. +The use and study of _logic_ involves finding a new fact by analyzing whether some other facts, when brought together, can prove that fact to be true. Some facts, or conditions, when looked at together may prove another fact to be true, or maybe false. -If the temperature outside is below freezing and you don't have a coat, you will feel cold. If you're not sick, then you will feel well. If you can swim or ride in a boat in water, you will stay afloat. These are statements of fact that result from some condition being true. +If the temperature outside is below freezing and you don't have a coat, you will feel cold. If you're not sick, then you will feel well. If you can swim or ride in a boat on water, you will stay afloat. These are statements of fact that result from some condition being true. ## Truth statements @@ -12,7 +12,7 @@ By taking some facts and putting them into a logical form, we can make an arithm * **NOT** ``sick`` **=** ``I feel well`` * ``I can swim`` **OR** ``I'm in a boat`` **=** ``I'm floating`` -You see the AND, NOT, and OR in the example word equations? These are our logical _operators_. Every day we make decisions when we think about one or more facts together using these operators. Sometimes, it's necessary for all facts to be true in order for the conclusion to be true. This is the case when the AND operator is used. When analyzing facts with the OR operator, only on fact needs to be true for the conclusion to be true also. +You see the AND, NOT, and OR in the example word equations? These are our logical _operators_. Every day we make decisions when we think about one or more facts together using these operators. Sometimes, it's necessary for all facts to be true in order for the conclusion to be true. This is the case when the AND operator is used. When analyzing facts with the OR operator, only one fact needs to be true for the conclusion to be true also. Making a decision may require more than just one or two facts. When this happens, another operator is needed to combine the facts together to make a conclusion. In the last example word equation, you actually might not be floating if just those two condtions are true. To correctly prove that you're actually floating, you need to state that you're in water too. @@ -109,7 +109,7 @@ Because you feel cold only when both conditions are true, the statement becomes ``A ¡ B`` = ``Q`` -A truth table for the variables in the expression have the same values as the table for the truth statement (``true`` and ``false`` are abbreviated to just ``T`` and ``F``). +A truth table for the Boolean variables in the expression have the same values as the table for the truth statement (``true`` and ``false`` are abbreviated to just ``T`` and ``F``). A | B | Q -|-|- @@ -140,3 +140,9 @@ T | F | F To write a Boolean equation for when you feel cold, we find the condtions in the table where ``Q`` is ``true``. Here we see that you will feel cold only in one row, when condition ``A`` is ``true`` and condtion ``B`` is ``false``. The Boolean equation for these conditions is this: ``A ¡ ~B`` = ``Q`` + +## ~button /courses/logic-lab/elements + +NEXT: Boolean Elements + +## ~ \ No newline at end of file diff --git a/docs/courses/logic-lab/logic-gates.md b/docs/courses/logic-lab/logic-gates.md index fc37475d2fe..f045bb6140c 100644 --- a/docs/courses/logic-lab/logic-gates.md +++ b/docs/courses/logic-lab/logic-gates.md @@ -2,7 +2,7 @@ ![OR gate symbol](/static/courses/logic-lab/logic-gates/full-adder.png) -In the real world digital devices aren't the abstract logical expressions of Boolean algebra, but they are implementations of these expressions in hardware. The logical expressions are translated into device structures called _logic gates_. A logic gate is both a symbolic representation of a logical operation and, when used in digital electronics, it can is an actual circuit in hardware. A single logic gate is usually made of several transistors an shares space with many others in an integrated circuit. +In the real world digital devices aren't the abstract logical expressions of Boolean algebra, but they are implementations of these expressions in hardware. The logical expressions are translated into device structures called _logic gates_. A logic gate is both a symbolic representation of a logical operation and, when used in digital electronics, it is an actual circuit in hardware. A single logic gate is usually made of several transistors an shares space with many others in an integrated circuit. Each of the basic operators we learned about in the [expressions](/courses/logic-lab/expressions) section have a gate symbol. The symbol takes the place of the operator and the variables are the inputs to the gate. The resulting value from the expression equation is the output of the gate. The output of a gate can be a final result or it can be connected as an input to yet another gate. @@ -85,3 +85,9 @@ When this equation is converted to logic gates, there's one fewer gate than in t ![Combinatorial XOR second version](/static/courses/logic-lab/logic-gates/combinatorial2-xor.png) This diagram has less complexity than the first one. Reduction in the number of gates to accomplish the same logical result is one of the primary goals for digital logic design. For electronic devices, this allows more gates to use the limited amount of space on an integrated circuit. + +## ~button /courses/logic-lab/programmable + +NEXT: Programmable Logic + +## ~ \ No newline at end of file diff --git a/docs/courses/logic-lab/programmable.md b/docs/courses/logic-lab/programmable.md index 32b357e0389..1b614cab7e2 100644 --- a/docs/courses/logic-lab/programmable.md +++ b/docs/courses/logic-lab/programmable.md @@ -135,9 +135,9 @@ if (pins.digitalReadPin(DigitalPin.P6) > 0) { You can test different input combinations by connecting the other ends of alligator clip leads on pins **P0** and **P1** to either **GND** or **3V**. The **GND** pin will make a ``false`` input value and **3V** will make a ``true`` input value. -If you have an expansion connector for your @boardname@, you can use the combined logic script and the logic observer code to check each ouptput. Move the other end alligator clip lead connected to the observer pin **P6** to each of the outputs **P2**, **P3**, and **P4** to see the result of the logic operation programmed for those pins. +If you have an expansion connector for your @boardname@, you can use the combined logic script and the logic observer code to check each ouptput. Move the other end of the alligator clip lead connected to the observer pin **P6** to each of the outputs **P2**, **P3**, and **P4** to see the result of the logic operation programmed for those pins. -If you just have the @boardname@ by itself, you can test each logic function using only the scripts for each logic gate. Just put the script inside a ``||loops:forever||`` and place a ``||basic:show string||`` block with the logic letter after each ``||pins:digital write pin||``. +If you just have the @boardname@ by itself, you can test each logic function using only the scripts for each logic gate. Just put the script inside a ``||loops:forever||`` loop and place a ``||basic:show string||`` block with the logic letter after each ``||pins:digital write pin||``. This is the code for the **NOT** gate: @@ -164,7 +164,7 @@ GND | 3V | ``[basic.showString("T")]`` 3V | GND | ``[basic.showString("F")]``
-Do test connections for the inputs and check the results for the **OR** and **AND** outputs. +Do test connections for the inputs, then check and record the results for the **OR** and **AND** outputs. #### OR truth table diff --git a/docs/courses/ucp-science.md b/docs/courses/ucp-science.md index 7de33954320..a43a380764d 100644 --- a/docs/courses/ucp-science.md +++ b/docs/courses/ucp-science.md @@ -10,7 +10,7 @@ These lessons guide the student in hands-on, practical measurement activities al ### ~hint -**Download it** +#### Download it The entire course is also available as a download. Choose any of these formats: @@ -31,7 +31,9 @@ The lesson series includes: * [Body Electrical & Waves](/courses/ucp-science/body-electrical) * [Electricity - Battery Tester](/courses/ucp-science/electricity) * [Rocket Acceleration](/courses/ucp-science/rocket-acceleration) - -The [Science Experiments](https://sites.google.com/view/utahcodingproject/csta/microbit-science-experiments) lesson series is generously provided by the [Utah Coding Project](https://sites.google.com/view/utahcodingproject/home) and is developed by [Carl Lyman](mailto:utahcoding@outlook.com). +* [Egg Drop Experiment](/courses/ucp-science/egg-drop) +* [Spoon Race](/courses/ucp-science/spoon-race) + +The [Science Experiments](https://sites.google.com/view/utahcodingproject/microbits/microbit-science-experiments) lesson series is generously provided by the [Utah Coding Project](https://sites.google.com/view/utahcodingproject/home) and is developed by [Carl Lyman](mailto:utahcoding@outlook.com). [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/88x31.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) diff --git a/docs/courses/ucp-science/SUMMARY.md b/docs/courses/ucp-science/SUMMARY.md index de39adc1a94..e264354e49d 100644 --- a/docs/courses/ucp-science/SUMMARY.md +++ b/docs/courses/ucp-science/SUMMARY.md @@ -58,3 +58,17 @@ * [Build](/courses/ucp-science/rocket-acceleration/build) * [Setup and procedure](/courses/ucp-science/rocket-acceleration/setup-procedure) * [Resources](/courses/ucp-science/rocket-acceleration/resources) + +## Egg Drop Experiment + +* [Egg Drop](/courses/ucp-science/egg-drop) + * [Overview](/courses/ucp-science/egg-drop/overview) + * [Setup and procedure](/courses/ucp-science/egg-drop/setup-procedure) + * [Resources](/courses/ucp-science/egg-drop/resources) + +## Egg and Spoon Race + +* [Spoon Race](/courses/ucp-science/spoon-race) + * [Overview](/courses/ucp-science/spoon-race/overview) + * [Setup and procedure](/courses/ucp-science/spoon-race/setup-procedure) + * [Resources](/courses/ucp-science/spoon-race/resources) diff --git a/docs/courses/ucp-science/body-electrical.md b/docs/courses/ucp-science/body-electrical.md index 1a9b85accd4..9bdc0310150 100644 --- a/docs/courses/ucp-science/body-electrical.md +++ b/docs/courses/ucp-science/body-electrical.md @@ -4,6 +4,14 @@ Electrical impulses in the body can be observed, measured, and recorded as waves to show that there is a relationship between the circulatory, respiratory, muscular, and nervous systems. The @boardname@ can measure and record these waves, then send them to a to another @boardname@ which serves as the data collection device. The data can then be downloaded and anaylzed in a spreadsheet. +## Lesson concept + +### Use the micro:bit to measure impulses from the human body + +Watch this short video to see how to use a micro:bit to sense muscle movements in the body. + +https://youtu.be/vxlPQZIwYRc + ## Contents * [Overview](/courses/ucp-science/body-electrical/overview) @@ -11,7 +19,4 @@ Electrical impulses in the body can be observed, measured, and recorded as waves * [Resources](/courses/ucp-science/body-electrical/resources)
- -| | | | -|-|-|-| -| Adapted from "[Body Electrical & Waves](https://drive.google.com/open?id=1KofuOt0v1lmQhQyJux1XWDVoCDeslcjDFysjStFmo1w)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | +Adapted from "[Body Electrical & Waves](https://drive.google.com/open?id=1KofuOt0v1lmQhQyJux1XWDVoCDeslcjDFysjStFmo1w)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) diff --git a/docs/courses/ucp-science/body-electrical/overview.md b/docs/courses/ucp-science/body-electrical/overview.md index 24dce921e77..7d23b0cdf21 100644 --- a/docs/courses/ucp-science/body-electrical/overview.md +++ b/docs/courses/ucp-science/body-electrical/overview.md @@ -39,8 +39,9 @@ Students will: * 2 long (36-48” or 100-130 cm) thin wires for electrical body sensors (wire from an old network cable works quite well). * Painters tape to tape the wires to the skin on the body. -
+## ~button /courses/ucp-science/body-electrical/setup-procedure +NEXT: Setup an Procedure +## ~ -| | | | -|-|-|-| -| Adapted from "[Body Electrical & Waves](https://drive.google.com/open?id=1KofuOt0v1lmQhQyJux1XWDVoCDeslcjDFysjStFmo1w)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | \ No newline at end of file +
+Adapted from "[Body Electrical & Waves](https://drive.google.com/open?id=1KofuOt0v1lmQhQyJux1XWDVoCDeslcjDFysjStFmo1w)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) \ No newline at end of file diff --git a/docs/courses/ucp-science/body-electrical/resources.md b/docs/courses/ucp-science/body-electrical/resources.md index 6db935a9c41..ad5edfce0c2 100644 --- a/docs/courses/ucp-science/body-electrical/resources.md +++ b/docs/courses/ucp-science/body-electrical/resources.md @@ -34,7 +34,7 @@ http://www.csteachers.org/page/standards. ## Utah Science with Engineering Education (SEEd) * [Utah Science Website](https://schools.utah.gov/curr/science) -* [Utah Grades 6-8 SEEd Standards](https://schools.utah.gov/file/265a0b53-b6a7-48fb-b253-b6a5f38ffe19) +* [Utah Grades 6-8 SEEd Standards](https://schools.utah.gov/File/5ec76f98-0844-4afc-8077-5d9401241e35) * [Sixth grade OER Science text](https://eq.uen.org/emedia/items/dae58176-b839-4b26-87e4-09ca5ed98875/1/Grade6RS.pdf) * [Seventh grade OER Science text](https://eq.uen.org/emedia/items/afd89ff1-054c-4ac5-a712-67f4c6029644/1/Grade7RS.pdf) * [Eighth grade OER Science text](https://eq.uen.org/emedia/items/e5219302-32b9-4c2f-ad65-38f303da6654/1/Grade8RS.pdf) @@ -81,7 +81,4 @@ http://www.csteachers.org/page/standards. * [Blog entry on Windows 10 MakeCode app](https://sites.google.com/view/utahcodingproject/blog/2018-jan-makecode-app)
- -| | | | -|-|-|-| -| Adapted from "[Body Electrical & Waves](https://drive.google.com/open?id=1KofuOt0v1lmQhQyJux1XWDVoCDeslcjDFysjStFmo1w)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | +Adapted from "[Body Electrical & Waves](https://drive.google.com/open?id=1KofuOt0v1lmQhQyJux1XWDVoCDeslcjDFysjStFmo1w)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) diff --git a/docs/courses/ucp-science/body-electrical/setup-procedure.md b/docs/courses/ucp-science/body-electrical/setup-procedure.md index 7c6a8cf62d3..a98f90663d0 100644 --- a/docs/courses/ucp-science/body-electrical/setup-procedure.md +++ b/docs/courses/ucp-science/body-electrical/setup-procedure.md @@ -122,11 +122,12 @@ Set up the experiment to collect data while someone is exercising. Research what about EKG and other body electrical signals. -
+# ~button /courses/ucp-science/body-electrical/resources +NEXT: Resources +## ~ -| | | | -|-|-|-| -| Adapted from "[Body Electrical & Waves](https://drive.google.com/open?id=1KofuOt0v1lmQhQyJux1XWDVoCDeslcjDFysjStFmo1w)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | +
+Adapted from "[Body Electrical & Waves](https://drive.google.com/open?id=1KofuOt0v1lmQhQyJux1XWDVoCDeslcjDFysjStFmo1w)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) ```package radio diff --git a/docs/courses/ucp-science/data-collection.md b/docs/courses/ucp-science/data-collection.md index d0fbdf60730..4f5284963cb 100644 --- a/docs/courses/ucp-science/data-collection.md +++ b/docs/courses/ucp-science/data-collection.md @@ -6,16 +6,12 @@ This lesson introduces the student to using the @boardname@ to take measurements ## Lesson concept -### ~ hint - -#### Data collection overview +### Data collection overview See how data is collected from the @boardname@, viewed, and analyzed in this video. https://youtu.be/tZy9Ev21B4c -### ~ - ## Contents * [Overview](/courses/ucp-science/data-collection/overview) @@ -23,7 +19,4 @@ https://youtu.be/tZy9Ev21B4c * [Resources](/courses/ucp-science/data-collection/resources)
- -| | | | -|-|-|-| -| Adapted from "[Microbit Data Collection Methods](https://drive.google.com/open?id=13Mi6caoelyzgch6tUj-wlw0bmgS7ikGEwYR2a37mEww)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | +Adapted from "[Microbit Data Collection Methods](https://drive.google.com/open?id=13Mi6caoelyzgch6tUj-wlw0bmgS7ikGEwYR2a37mEww)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) diff --git a/docs/courses/ucp-science/data-collection/overview.md b/docs/courses/ucp-science/data-collection/overview.md index 5fc8c717cc4..f488b34ffec 100644 --- a/docs/courses/ucp-science/data-collection/overview.md +++ b/docs/courses/ucp-science/data-collection/overview.md @@ -54,8 +54,9 @@ Students will: * A longer USB @boardname@ cable * Spreadsheet program for data analysis -
+## ~button /courses/ucp-science/data-collection/setup-procedure +NEXT: Setup an Procedure +## ~ -| | | | -|-|-|-| -| Adapted from "[Microbit Data Collection Methods](https://drive.google.com/open?id=13Mi6caoelyzgch6tUj-wlw0bmgS7ikGEwYR2a37mEww)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | +
+Adapted from "[Microbit Data Collection Methods](https://drive.google.com/open?id=13Mi6caoelyzgch6tUj-wlw0bmgS7ikGEwYR2a37mEww)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) diff --git a/docs/courses/ucp-science/data-collection/resources.md b/docs/courses/ucp-science/data-collection/resources.md index 91f2b641038..ae2808bf022 100644 --- a/docs/courses/ucp-science/data-collection/resources.md +++ b/docs/courses/ucp-science/data-collection/resources.md @@ -47,7 +47,4 @@ http://www.csteachers.org/page/standards. * [Blog entry on Windows 10 MakeCode app](https://sites.google.com/view/utahcodingproject/blog/2018-jan-makecode-app)
- -| | | | -|-|-|-| -| Adapted from "[Microbit Data Collection Methods](https://drive.google.com/open?id=13Mi6caoelyzgch6tUj-wlw0bmgS7ikGEwYR2a37mEww)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | +Adapted from "[Microbit Data Collection Methods](https://drive.google.com/open?id=13Mi6caoelyzgch6tUj-wlw0bmgS7ikGEwYR2a37mEww)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) diff --git a/docs/courses/ucp-science/data-collection/setup-procedure.md b/docs/courses/ucp-science/data-collection/setup-procedure.md index 836972b4e17..3e53d7c779d 100644 --- a/docs/courses/ucp-science/data-collection/setup-procedure.md +++ b/docs/courses/ucp-science/data-collection/setup-procedure.md @@ -178,17 +178,20 @@ The **Download** button in the red highlighted box allows the downloading of abo When the data recorded is downloaded as a CSV spreadsheet file, it is named ``"data.csv"``. -#### ~hint +### ~hint + +#### Where's the CSV? The CSV file usually opens in directly into a spreadsheet but sometimes it doesn’t which makes it hard to find. A search of the ``C:\`` drive might be necessary to find it. -#### ~ +### ~ + +## ~button /courses/ucp-science/data-collection/resources +NEXT: Resources +## ~
- -| | | | -|-|-|-| -| Adapted from "[Microbit Data Collection Methods](https://drive.google.com/open?id=13Mi6caoelyzgch6tUj-wlw0bmgS7ikGEwYR2a37mEww)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | +Adapted from "[Microbit Data Collection Methods](https://drive.google.com/open?id=13Mi6caoelyzgch6tUj-wlw0bmgS7ikGEwYR2a37mEww)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) ```package diff --git a/docs/courses/ucp-science/egg-drop.md b/docs/courses/ucp-science/egg-drop.md new file mode 100644 index 00000000000..0655081cb8e --- /dev/null +++ b/docs/courses/ucp-science/egg-drop.md @@ -0,0 +1,20 @@ +# Egg Drop Experiment + +Learn how to modernize this age-old science experiment using the micro:bit to measure acceleration before breaking too many eggs! + +## Lesson concept + +### Use the micro:bit to measure force + +Watch this short video to see how to use a micro:bit to detect the force from a fall. + +https://youtu.be/tnDJFdC3Nd4 + +## Contents + +* [Overview](/courses/ucp-science/egg-drop/overview) +* [Setup and procedure](/courses/ucp-science/egg-drop/setup-procedure) +* [Resources](/courses/ucp-science/egg-drop/resources) + +
+Contributed by the [Utah Coding Project](https://sites.google.com/view/utahcodingproject) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) \ No newline at end of file diff --git a/docs/courses/ucp-science/egg-drop/overview.md b/docs/courses/ucp-science/egg-drop/overview.md new file mode 100644 index 00000000000..4cf9f01a208 --- /dev/null +++ b/docs/courses/ucp-science/egg-drop/overview.md @@ -0,0 +1,49 @@ +# Overview + +## Science concept + +When an object is dropped from a height, it follows Newton's Laws of Motion and is pulled down by the earth’s gravitational force. When the egg hits the ground, it is a collision between the Earth and the Egg. + +Let’s review Newton’s 3 Laws of Motion as they relate to our Egg Drop experiment. + +**Newton’s 1st Law of Motion**: A body in motion remains in motion, or a body at rest remains at rest, unless acted upon by a force. This implies that once we drop the egg, if there was no ground to stop it, the egg would fall forever. + +![Newton's First Law](/static/courses/ucp-science/egg-drop/newton-1st-law.png) + +**Newton’s 2nd Law of Motion**: Force equals mass times acceleration: F = m * a. Using this equation, we can calculate the Earth’s gravitational force to be equal to the mass of the egg times acceleration of gravity, which is a constant of approximately 9.8 meters per second squared. + +![Newton's Second Law](/static/courses/ucp-science/egg-drop/newton-2nd-law.png) + +**Newton’s 3rd Law of Motion**: For every action, there is an equal and opposite reaction. In this case, the egg will be exerting force downwards, so when it comes in contact with the ground, it will experience the ground exerting force upwards. The two objects (the egg and the earth) will collide and both will experience equal and opposite forces. However, since the earth is so much bigger than the egg, the force on the earth will be minimal, while the force on the egg will be very strong and may cause the shell of the egg to crack and break. + +![Newton's Third Law](/static/courses/ucp-science/egg-drop/newton-3rd-law.png) + +## Project Goal + +Give students real world experience with coding, collecting data, analyzing data, and reporting results using MakeCode’s block programming and a micro:bit with its sensors. + +## Prior Knowledge + +Students need to have a basic knowledge of how to code using block style programming and download a program to a micro:bit using MakeCode. + +## Student Outcomes + +The objective of the egg drop experiment is to keep the egg from breaking as it decelerates. It becomes clear from Newton's Laws of Motion that in order to minimize the force experienced by the egg at impact, students designing the egg carriers must increase the time over which the egg is brought to rest or decrease the egg's velocity at the time of the crash. + +Students will: + +* Understand the Laws of Motion and Gravitational Force. +* Design a carrier for their egg that will minimize the force exerted on the egg when colliding with the ground. +* Code the micro:bit to test the strength of the force. +* Iterate on their designs based on the results of the micro:bit data. + +## Materials Needed + +* A micro:bit, micro-USB cable and battery pack +* A computer with internet access +* Crafting materials to use for the egg carrier – these may include cardboard cups/boxes, cotton, plastic bags or bottles, string, straws, popsicle sticks, tissue paper, bubble wrap, glue, tape +* An uncooked egg + +## ~button /courses/ucp-science/egg-drop/setup-procedure +NEXT: Setup an Procedure +## ~ \ No newline at end of file diff --git a/docs/courses/ucp-science/egg-drop/resources.md b/docs/courses/ucp-science/egg-drop/resources.md new file mode 100644 index 00000000000..b874ba541e8 --- /dev/null +++ b/docs/courses/ucp-science/egg-drop/resources.md @@ -0,0 +1,24 @@ +# Resources + +## CSTA Standards + +https://csteachers.org/k12standards + +### Computing Systems + +* 02 - Design projects that combine hardware and software components to collect and exchange data. +* 03 - Systematically identify and fix problems with computing devices and their components. + +### Data & Analysis + +* 07 - Represent data using multiple encoding schemes. +* 08 - Collect data using computational tools and transform the data to make it more useful and reliable. +* 09 - Refine computational models based on the data they have generated. + +## Other Resources + +* Micro:bit Accelerometer Overview - https://youtu.be/UT35ODxvmS0 +* Behind the MakeCode Hardware: Accelerometer - https://youtu.be/byngcwjO51U +* Microbit.org Classroom Resources - https://microbit.org/teach/classroom-resources +* MakeCode Reference Documentation - https://makecode.microbit.org/reference +* Utah Coding Project - https://sites.google.com/view/utahcodingproject \ No newline at end of file diff --git a/docs/courses/ucp-science/egg-drop/setup-procedure.md b/docs/courses/ucp-science/egg-drop/setup-procedure.md new file mode 100644 index 00000000000..11c28d0f20c --- /dev/null +++ b/docs/courses/ucp-science/egg-drop/setup-procedure.md @@ -0,0 +1,94 @@ +# Setup and procedure + +## Setup + +* Review Newton’s Laws of Motion and make some predictions about what will happen if you drop objects with different mass and acceleration. +* Create hypotheses around what types of designs might minimize force of collision. +* Group students to begin work on egg carriers. +* Code the micro:bit and perform a series of tests dropping the micro:bit in the carriers from a height. +* Once the test results are successful, insert the egg into the carriers and drop from a height. +* Debrief on the results - which carriers were the most successful? Why? + +## Code + +This project will use the micro:bit to test the force of collision. + +* From the ``||input:Input||`` Toolbox drawer, drag an ``||input:on shake||`` block to the workspace +* Use the drop-down menu to select 8g. This block will detect when a force 8g or greater is exerted on the micro:bit. + +```blocks +input.onGesture(Gesture.EightG, function () { + +}) +``` + +### ~ hint + +#### Gravitational Force + +The g-force or gravitational force is a measure of gravitational force where an object at rest on the Earth's surface is subject to 1 g of force. So, 8g = 8 times the normal gravitational force exerted on an object. +### ~ + +* From the ``||basic:Basic||`` Toolbox drawer, drag a ``||basic:show leds||`` block and drop it into the ``||input:on 8g||`` block. +* Draw an X or other symbol to indicate that the micro:bit has experienced 8g of force. + +```blocks +input.onGesture(Gesture.EightG, function () { +basic.showLeds(` +# . . . # +. # . # . +. . # . . +. # . # . +# . . . # +`) +}) +``` + +Now let’s add some code to reset our experiment. + +* From the ``||input:Input||`` Toolbox drawer, drag an ``||input:on button pressed||`` block to the workspace. + +```blocks +input.onButtonPressed(Button.A, function () { + +}) +``` + +* From the ``||basic:Basic||`` Toolbox drawer, drag a ``||basic:clear screen||`` block and drop it into the ``||input:on button pressed||`` block. + +```blocks +input.onGesture(Gesture.EightG, function () { +basic.showLeds(` +# . . . # +. # . # . +. . # . . +. # . # . +# . . . # +`) +}) +input.onButtonPressed(Button.A, function () { +basic.clearScreen() +}) +``` + +Sample code file: https://makecode.microbit.org/_L96ELqWtrV65 + +Download the code onto the micro:bit, and then connect the micro:bit to a battery pack. + +## Conducting the Experiment + +After coding the micro:bit and constructing the egg drop carriers, take turns testing dropping the micro:bit in the carriers from a height. Do the micro:bit lights turn on? If so, that means the force exerted on the micro:bit was at least 8g – a good indication that the egg most likely will break on impact. Continue refining the egg drop carriers until no micro:bit lights turn on when dropped. Then test with an egg! + +## Debrief + +Discuss the results of the experiment: + +* Which egg carriers were successful? Which were not? +* Are there patterns you can identify between the carrier designs? +* Were the micro:bit test results a good indication of whether the egg would break or not? +* How might you find out exactly how much g-force would need to be exerted to break the egg? +* Thinking about Newton’s Laws of Motion, what principles can you deduce about how to minimize the force of impact? + +## ~button /courses/ucp-science/egg-drop/resources +NEXT: Resources +## ~ \ No newline at end of file diff --git a/docs/courses/ucp-science/electricity.md b/docs/courses/ucp-science/electricity.md index 295bae9cbd9..eee5b3d5a1e 100644 --- a/docs/courses/ucp-science/electricity.md +++ b/docs/courses/ucp-science/electricity.md @@ -4,6 +4,14 @@ This lesson observes the force of electricity. The charge in several batteries is measured by the micro:bit to see how much electric force is present in each one. The results are recorded to analyze the condition of each battery. As an application of the experiment, batteries in poor condition (mostly discharged) can be noted and properly disposed of. +## Lesson concept + +### Use the micro:bit to measure the charge of a battery + +Watch this short video to see how to use a micro:bit to check the remaining charge in a battery. + +https://youtu.be/gdlc34nhjK4 + ## Contents * [Overview](/courses/ucp-science/electricity/overview) @@ -11,7 +19,4 @@ This lesson observes the force of electricity. The charge in several batteries i * [Resources](/courses/ucp-science/electricity/resources)
- -| | | | -|-|-|-| -| Adapted from "[Electricity - Battery Tester](https://drive.google.com/open?id=15Xry9jFsIzHHG7RpaIomLodl9pBjTiKDvtjkd227b7Y)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | +Adapted from "[Electricity - Battery Tester](https://drive.google.com/open?id=15Xry9jFsIzHHG7RpaIomLodl9pBjTiKDvtjkd227b7Y)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) diff --git a/docs/courses/ucp-science/electricity/overview.md b/docs/courses/ucp-science/electricity/overview.md index f5a885ff966..9980b5717ca 100644 --- a/docs/courses/ucp-science/electricity/overview.md +++ b/docs/courses/ucp-science/electricity/overview.md @@ -34,8 +34,9 @@ Students will: * Spreadsheet for data analysis * Old batteries for testing -
+## ~button /courses/ucp-science/electricity/setup-procedure +NEXT: Setup an Procedure +## ~ -| | | | -|-|-|-| -| Adapted from "[Electricity - Battery Tester](https://drive.google.com/open?id=15Xry9jFsIzHHG7RpaIomLodl9pBjTiKDvtjkd227b7Y)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | +
+Adapted from "[Electricity - Battery Tester](https://drive.google.com/open?id=15Xry9jFsIzHHG7RpaIomLodl9pBjTiKDvtjkd227b7Y)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) diff --git a/docs/courses/ucp-science/electricity/resources.md b/docs/courses/ucp-science/electricity/resources.md index 45232545819..9c2471918e1 100644 --- a/docs/courses/ucp-science/electricity/resources.md +++ b/docs/courses/ucp-science/electricity/resources.md @@ -59,7 +59,4 @@ Strand 7.1: Forces are Interactions between Matter * [Blog entry on Windows 10 MakeCode app](https://sites.google.com/view/utahcodingproject/blog/2018-jan-makecode-app)
- -| | | | -|-|-|-| -| Adapted from "[Electricity - Battery Tester](https://drive.google.com/open?id=15Xry9jFsIzHHG7RpaIomLodl9pBjTiKDvtjkd227b7Y)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | +Adapted from "[Electricity - Battery Tester](https://drive.google.com/open?id=15Xry9jFsIzHHG7RpaIomLodl9pBjTiKDvtjkd227b7Y)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) diff --git a/docs/courses/ucp-science/electricity/setup-procedure.md b/docs/courses/ucp-science/electricity/setup-procedure.md index e9b99e9397c..2b759d31bfc 100644 --- a/docs/courses/ucp-science/electricity/setup-procedure.md +++ b/docs/courses/ucp-science/electricity/setup-procedure.md @@ -112,8 +112,9 @@ Set up an experiment using a loop of copper wires and see if the micro:bit can d Log each battery tested to make decision on which of the batteries are good and which ones need to be disposed of. -
+## ~button /courses/ucp-science/electricity/resources +NEXT: Resources +## ~ -| | | | -|-|-|-| -| Adapted from "[Electricity - Battery Tester](https://drive.google.com/open?id=15Xry9jFsIzHHG7RpaIomLodl9pBjTiKDvtjkd227b7Y)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | +
+Adapted from "[Electricity - Battery Tester](https://drive.google.com/open?id=15Xry9jFsIzHHG7RpaIomLodl9pBjTiKDvtjkd227b7Y)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) diff --git a/docs/courses/ucp-science/gravity.md b/docs/courses/ucp-science/gravity.md index 8a724db981b..00b2fafb90f 100644 --- a/docs/courses/ucp-science/gravity.md +++ b/docs/courses/ucp-science/gravity.md @@ -12,7 +12,4 @@ Gravity is the attraction of one particle or body to another. Larger masses have * [Resources](/courses/ucp-science/gravity/resources)
- -| | | | -|-|-|-| -| Adapted from "[Gravity, Motion, and Waves](https://drive.google.com/open?id=1Z8S-W3n1jX6drC8ALj8Wh1Rjc0CyP0Afs3acnIjDYes)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | +Adapted from "[Gravity, Motion, and Waves](https://drive.google.com/open?id=1Z8S-W3n1jX6drC8ALj8Wh1Rjc0CyP0Afs3acnIjDYes)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) diff --git a/docs/courses/ucp-science/gravity/overview.md b/docs/courses/ucp-science/gravity/overview.md index 7819f34d7b9..23e61578b7c 100644 --- a/docs/courses/ucp-science/gravity/overview.md +++ b/docs/courses/ucp-science/gravity/overview.md @@ -39,8 +39,9 @@ Students will: * Spreadsheet for data analysis * Padding for one @boardname@ for gravity testing -
+## ~button /courses/ucp-science/gravity/setup-procedure +NEXT: Setup an Procedure +## ~ -| | | | -|-|-|-| -| Adapted from "[Gravity, Motion, and Waves](https://drive.google.com/open?id=1Z8S-W3n1jX6drC8ALj8Wh1Rjc0CyP0Afs3acnIjDYes)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | +
+Adapted from "[Gravity, Motion, and Waves](https://drive.google.com/open?id=1Z8S-W3n1jX6drC8ALj8Wh1Rjc0CyP0Afs3acnIjDYes)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) diff --git a/docs/courses/ucp-science/gravity/resources.md b/docs/courses/ucp-science/gravity/resources.md index df88a979a09..ea19d400128 100644 --- a/docs/courses/ucp-science/gravity/resources.md +++ b/docs/courses/ucp-science/gravity/resources.md @@ -69,7 +69,4 @@ Forces are push or pull interactions between two objects. Changes in motion, bal * [Blog entry on Windows 10 MakeCode app](https://sites.google.com/view/utahcodingproject/blog/2018-jan-makecode-app)
- -| | | | -|-|-|-| -| Adapted from "[Gravity, Motion, and Waves](https://drive.google.com/open?id=1Z8S-W3n1jX6drC8ALj8Wh1Rjc0CyP0Afs3acnIjDYes)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | +Adapted from "[Gravity, Motion, and Waves](https://drive.google.com/open?id=1Z8S-W3n1jX6drC8ALj8Wh1Rjc0CyP0Afs3acnIjDYes)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) diff --git a/docs/courses/ucp-science/gravity/setup-procedure.md b/docs/courses/ucp-science/gravity/setup-procedure.md index c33982d6a79..aaf708e9d9f 100644 --- a/docs/courses/ucp-science/gravity/setup-procedure.md +++ b/docs/courses/ucp-science/gravity/setup-procedure.md @@ -107,11 +107,12 @@ Earthquakes cause vibrations which can be detected with the Microbit accelerator Use the @boardname@s to record data from a skater at a skate park or acceleration down a ramp like a Pinewood Derby car. -
+## ~button /courses/ucp-science/gravity/resources +NEXT: Resources +## ~ -| | | | -|-|-|-| -| Adapted from "[Gravity, Motion, and Waves](https://drive.google.com/open?id=1Z8S-W3n1jX6drC8ALj8Wh1Rjc0CyP0Afs3acnIjDYes)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | +
+Adapted from "[Gravity, Motion, and Waves](https://drive.google.com/open?id=1Z8S-W3n1jX6drC8ALj8Wh1Rjc0CyP0Afs3acnIjDYes)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) ```package radio diff --git a/docs/courses/ucp-science/population.md b/docs/courses/ucp-science/population.md index d4f5ccdbe4a..6ea3aeeb362 100644 --- a/docs/courses/ucp-science/population.md +++ b/docs/courses/ucp-science/population.md @@ -4,16 +4,12 @@ Patterns occur everywhere in nature. Certain characteristics in a populations ar ## Lesson concept -### ~ hint - -#### Population trait experiment example +### Population trait experiment example Watch this video about how to perform a population trait experiment. https://youtu.be/NNZEMiJHY2o -### ~ - ## Contents * [Overview](/courses/ucp-science/population/overview) @@ -21,7 +17,4 @@ https://youtu.be/NNZEMiJHY2o * [Resources](/courses/ucp-science/population/resources)
- -| | | | -|-|-|-| -| Adapted from "[Population Trait Data Counter](https://drive.google.com/open?id=1CC5uhIoZK4Q67vU5Ldwna6GEeZYXNDYzgO8BUUjPuwI)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | +Adapted from "[Population Trait Data Counter](https://drive.google.com/open?id=1CC5uhIoZK4Q67vU5Ldwna6GEeZYXNDYzgO8BUUjPuwI)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) diff --git a/docs/courses/ucp-science/population/microbit-display.jpg b/docs/courses/ucp-science/population/microbit-display.jpg deleted file mode 100644 index 395f0274f20..00000000000 Binary files a/docs/courses/ucp-science/population/microbit-display.jpg and /dev/null differ diff --git a/docs/courses/ucp-science/population/overview.md b/docs/courses/ucp-science/population/overview.md index 0996462c01c..81614a8acb3 100644 --- a/docs/courses/ucp-science/population/overview.md +++ b/docs/courses/ucp-science/population/overview.md @@ -36,8 +36,9 @@ Students will: * 1 @boardname@ with battery connected * Windows 10 MakeCode app or [MakeCode](@homeurl@) in a browser. -
+## ~button /courses/ucp-science/population/setup-procedure +NEXT: Setup an Procedure +## ~ -| | | | -|-|-|-| -| Adapted from "[Population Trait Data Counter](https://drive.google.com/open?id=1CC5uhIoZK4Q67vU5Ldwna6GEeZYXNDYzgO8BUUjPuwI)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | \ No newline at end of file +
+Adapted from "[Population Trait Data Counter](https://drive.google.com/open?id=1CC5uhIoZK4Q67vU5Ldwna6GEeZYXNDYzgO8BUUjPuwI)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) \ No newline at end of file diff --git a/docs/courses/ucp-science/population/resources.md b/docs/courses/ucp-science/population/resources.md index fd278996a9d..a059745dacc 100644 --- a/docs/courses/ucp-science/population/resources.md +++ b/docs/courses/ucp-science/population/resources.md @@ -42,7 +42,4 @@ http://www.csteachers.org/page/standards. * [Blog entry on Windows 10 MakeCode app](https://sites.google.com/view/utahcodingproject/blog/2018-jan-makecode-app)
- -| | | | -|-|-|-| -| Adapted from "[Population Trait Data Counter](https://drive.google.com/open?id=1CC5uhIoZK4Q67vU5Ldwna6GEeZYXNDYzgO8BUUjPuwI)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | \ No newline at end of file +Adapted from "[Population Trait Data Counter](https://drive.google.com/open?id=1CC5uhIoZK4Q67vU5Ldwna6GEeZYXNDYzgO8BUUjPuwI)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) \ No newline at end of file diff --git a/docs/courses/ucp-science/population/setup-procedure.md b/docs/courses/ucp-science/population/setup-procedure.md index 75375744f9a..f928fcf67b2 100644 --- a/docs/courses/ucp-science/population/setup-procedure.md +++ b/docs/courses/ucp-science/population/setup-procedure.md @@ -27,7 +27,7 @@ https://youtu.be/NNZEMiJHY2o ### on Start event 1. Name the project, “Population Trait Counter”. -2. The ``||basic:on Start||`` event will display the title and purpose of the microbit in all caps, “POPULATION TRAIT COUNTER”. The text is put in the ``||basic:show string||`` block (the title is put in the ``||basic:on start||`` event so when the microbit is started up it will show what it is programmed to do. It is done in all CAPS because it is easier to read as it is displayed in the LED display). +2. The ``||basic:on start||`` event will display the title and purpose of the microbit in all caps, “POPULATION TRAIT COUNTER”. The text is put in the ``||basic:show string||`` block (the title is put in the ``||basic:on start||`` event so when the microbit is started up it will show what it is programmed to do. It is done in all CAPS because it is easier to read as it is displayed in the LED display). 3. From the ``||variables:Variables||`` toolbox create variables named ``trait1``, ``trait2``, and ``total``. These will be used as counters to keep track of the for each trait counted. Variables are named to describe what they will be storing. Variables are usually named by using lowercase letters and/or digits. If it is a 2 word name, it is usually named using camelCaps (no spaces but a capital where the second word starts. Examples: ``totalCount``, ``randNumber``, etc.) ```blocks @@ -113,7 +113,7 @@ input.onGesture(Gesture.Shake, () => { ### ~hint -**Warning** +#### Warning This procedure could be problematic if the @boardname@ is shaken to much while it is used in counting. @@ -123,8 +123,9 @@ This procedure could be problematic if the @boardname@ is shaken to much while i This project could easily be modified to keep track of scores for 2 different teams. What other ideas can you think of that counters could be used for? -
+## ~button /courses/ucp-science/population/resources +NEXT: Resources +## ~ -| | | | -|-|-|-| -| Adapted from "[Population Trait Data Counter](https://drive.google.com/open?id=1CC5uhIoZK4Q67vU5Ldwna6GEeZYXNDYzgO8BUUjPuwI)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | \ No newline at end of file +
+Adapted from "[Population Trait Data Counter](https://drive.google.com/open?id=1CC5uhIoZK4Q67vU5Ldwna6GEeZYXNDYzgO8BUUjPuwI)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) \ No newline at end of file diff --git a/docs/courses/ucp-science/rocket-acceleration.md b/docs/courses/ucp-science/rocket-acceleration.md index 98a5cef331e..8460da76d84 100644 --- a/docs/courses/ucp-science/rocket-acceleration.md +++ b/docs/courses/ucp-science/rocket-acceleration.md @@ -4,6 +4,14 @@ The Earth exerts a gravitational force on all objects. A rocket must have a force greater than gravity to lift off. This force, acceleration, can be measured with a @boardname@ in 3 different directions or as a combined force of all three. A rocket made from a two liter soda bottle is made as a test vehicle to measure changes in acceleration as it lifts off and falls back to the earth. +## Lesson concept + +### Use the micro:bit to measure the acceleration of a rocket + +Watch this short video to see how to use a micro:bit to record rocket acceleration. + +https://youtu.be/m9ntqxh8FvQ + ## Contents * [Overview](/courses/ucp-science/rocket-acceleration/overview) @@ -12,7 +20,4 @@ The Earth exerts a gravitational force on all objects. A rocket must have a forc * [Resources](/courses/ucp-science/rocket-acceleration/resources)
- -| | | | -|-|-|-| -| Adapted from "[Rocket Acceleration z Radios](https://drive.google.com/open?id=1IyhCPdYQevKh3kHNgukSxlgdvZIKuzmIBjLSRnFS36o)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | +Adapted from "[Rocket Acceleration z Radios](https://drive.google.com/open?id=1IyhCPdYQevKh3kHNgukSxlgdvZIKuzmIBjLSRnFS36o)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) diff --git a/docs/courses/ucp-science/rocket-acceleration/build.md b/docs/courses/ucp-science/rocket-acceleration/build.md index 51942346a5f..afb2f641c3a 100644 --- a/docs/courses/ucp-science/rocket-acceleration/build.md +++ b/docs/courses/ucp-science/rocket-acceleration/build.md @@ -6,8 +6,8 @@ The steps here show how to build a two liter soda bottle rocket. The @boardname@ ### Rocket construction -| | | -|-|-| +| | | | +|-|-|-| | **(1)** Find two 2-liter pop bottles to build the rocket and nose cone.| | ![two liter bottles](/static/courses/ucp-science/rocket-acceleration/two-liter-bottles.jpg) | | **(2)** Attach the fins cut from a plastic strawberry container.| | ![rocket fins](/static/courses/ucp-science/rocket-acceleration/rocket-fins.jpg)| | **(3)** Paint the 2-liter rocket after the fins are attached.| | ![Painted rocket](/static/courses/ucp-science/rocket-acceleration/painted-rocket.jpg)| @@ -22,23 +22,24 @@ The steps here show how to build a two liter soda bottle rocket. The @boardname@ In order to launch the rocket, you need to deliver compressed air to the rocket. There are several ways to make the rocket launcher. Here are some instructions and videos describing ways to do this. -#### ~ hint +### ~ hint -**Caution!** +#### Caution! The bottle rocket is launched when enough pressure builds up to push it off the launcher base. You don't always know when exaclty enough pressure exists to push the rocket up. To avoid being hit by the rocket, don't stand too close (you and anyone watching, and especially, don't stand directly over the rocket!) to it while you're adding pressure to the launcher. It may launch with enough force to hurt you if you're hit by it! -#### ~ +### ~ * [Air Command Water Rocket instructions](http://www.aircommandrockets.com/rocket_launcher.htm) (shows a variety of instructions) * [How To Build The Simplest Water Bottle Rocket Launcher](https://www.youtube.com/watch?v=gyOzvqmUs4c) * [Making a Water Bottle Rocket Launcher](https://www.youtube.com/watch?v=gDN9lxgzPlo) * [Weekend Project: Compressed Air Rocket](https://www.youtube.com/watch?v=eNFfK5uo6D0) -
+## ~button /courses/ucp-science/rocket-acceleration/setup-procedure +NEXT: Setup and Procedure +## ~ -| | | | -|-|-|-| -| Adapted from "[Rocket Acceleration z Radios](https://drive.google.com/open?id=1IyhCPdYQevKh3kHNgukSxlgdvZIKuzmIBjLSRnFS36o)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | +
+Adapted from "[Rocket Acceleration z Radios](https://drive.google.com/open?id=1IyhCPdYQevKh3kHNgukSxlgdvZIKuzmIBjLSRnFS36o)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) diff --git a/docs/courses/ucp-science/rocket-acceleration/overview.md b/docs/courses/ucp-science/rocket-acceleration/overview.md index 2608ab90c1f..a9e3f5cfa36 100644 --- a/docs/courses/ucp-science/rocket-acceleration/overview.md +++ b/docs/courses/ucp-science/rocket-acceleration/overview.md @@ -39,9 +39,10 @@ Students will: * A longer USB @boardname@ cable * Spreadsheet for data analysis -
+## ~button /courses/ucp-science/rocket-acceleration/build +NEXT: Build +## ~ -| | | | -|-|-|-| -| Adapted from "[Rocket Acceleration z Radios](https://drive.google.com/open?id=1IyhCPdYQevKh3kHNgukSxlgdvZIKuzmIBjLSRnFS36o)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | +
+Adapted from "[Rocket Acceleration z Radios](https://drive.google.com/open?id=1IyhCPdYQevKh3kHNgukSxlgdvZIKuzmIBjLSRnFS36o)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) diff --git a/docs/courses/ucp-science/rocket-acceleration/resources.md b/docs/courses/ucp-science/rocket-acceleration/resources.md index 0fc6ccb6952..090945be5f2 100644 --- a/docs/courses/ucp-science/rocket-acceleration/resources.md +++ b/docs/courses/ucp-science/rocket-acceleration/resources.md @@ -60,7 +60,4 @@ Forces are push or pull interactions between two objects. Changes in motion, bal * [Blog entry on Windows 10 MakeCode app](https://sites.google.com/view/utahcodingproject/blog/2018-jan-makecode-app)
- -| | | | -|-|-|-| -| Adapted from "[Rocket Acceleration z Radios](https://drive.google.com/open?id=1IyhCPdYQevKh3kHNgukSxlgdvZIKuzmIBjLSRnFS36o)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | +Adapted from "[Rocket Acceleration z Radios](https://drive.google.com/open?id=1IyhCPdYQevKh3kHNgukSxlgdvZIKuzmIBjLSRnFS36o)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) diff --git a/docs/courses/ucp-science/rocket-acceleration/setup-procedure.md b/docs/courses/ucp-science/rocket-acceleration/setup-procedure.md index 9b4980be320..b220fdce1ae 100644 --- a/docs/courses/ucp-science/rocket-acceleration/setup-procedure.md +++ b/docs/courses/ucp-science/rocket-acceleration/setup-procedure.md @@ -115,12 +115,13 @@ Set up the experiment to collect data when a @boardname@ is drown several feet o Research what acceleration on a skateboard at a skatepark or other types of movement as in a car. What about a ride at an amusement park? -```package -radio -``` +## ~button /courses/ucp-science/rocket-acceleration/resources +NEXT: Resources +## ~
+Adapted from "[Rocket Acceleration z Radios](https://drive.google.com/open?id=1IyhCPdYQevKh3kHNgukSxlgdvZIKuzmIBjLSRnFS36o)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) -| | | | -|-|-|-| -| Adapted from "[Rocket Acceleration z Radios](https://drive.google.com/open?id=1IyhCPdYQevKh3kHNgukSxlgdvZIKuzmIBjLSRnFS36o)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | +```package +radio +``` \ No newline at end of file diff --git a/docs/courses/ucp-science/soil-moisture.md b/docs/courses/ucp-science/soil-moisture.md index 172acb56c6f..4855e420a7d 100644 --- a/docs/courses/ucp-science/soil-moisture.md +++ b/docs/courses/ucp-science/soil-moisture.md @@ -6,17 +6,12 @@ Not only does water flow across the ground and into soil, water also moves throu ## Lesson concept -### ~ hint - -#### Soil moisture experiment video +### Soil moisture experiment video Watch this video about to see how to conduct a soil moisture experiment for plants. https://youtu.be/n0WRQf11Pzo -### ~ - - ## Contents * [Overview](/courses/ucp-science/soil-moisture/overview) @@ -24,7 +19,4 @@ https://youtu.be/n0WRQf11Pzo * [Resources](/courses/ucp-science/soil-moisture/resources)
- -| | | | -|-|-|-| -| Adapted from "[Soil Moisture Tester](https://drive.google.com/open?id=1Rv4oPoxrggbokczbroQUl-10py3_5fQjVxOvwHR_5I4)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | +Adapted from "[Soil Moisture Tester](https://drive.google.com/open?id=1Rv4oPoxrggbokczbroQUl-10py3_5fQjVxOvwHR_5I4)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) diff --git a/docs/courses/ucp-science/soil-moisture/overview.md b/docs/courses/ucp-science/soil-moisture/overview.md index 37d838cfd9b..2a0866563dd 100644 --- a/docs/courses/ucp-science/soil-moisture/overview.md +++ b/docs/courses/ucp-science/soil-moisture/overview.md @@ -32,8 +32,9 @@ Students will: * Spreadsheet for data analysis * Word processor for reporting results -
+## ~button /courses/ucp-science/soil-moisture/setup-procedure +NEXT: Setup and Procedure +## ~ -| | | | -|-|-|-| -| Adapted from "[Soil Moisture Tester](https://drive.google.com/open?id=1Rv4oPoxrggbokczbroQUl-10py3_5fQjVxOvwHR_5I4)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | +
+Adapted from "[Soil Moisture Tester](https://drive.google.com/open?id=1Rv4oPoxrggbokczbroQUl-10py3_5fQjVxOvwHR_5I4)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) diff --git a/docs/courses/ucp-science/soil-moisture/resources.md b/docs/courses/ucp-science/soil-moisture/resources.md index 5251e6e0b5d..3c34730dce4 100644 --- a/docs/courses/ucp-science/soil-moisture/resources.md +++ b/docs/courses/ucp-science/soil-moisture/resources.md @@ -71,7 +71,4 @@ The study of ecosystems includes the interaction of organisms with each other an * [Blog entry on Windows 10 MakeCode app](https://sites.google.com/view/utahcodingproject/blog/2018-jan-makecode-app)
- -| | | | -|-|-|-| -| Adapted from "[Soil Moisture Tester](https://drive.google.com/open?id=1Rv4oPoxrggbokczbroQUl-10py3_5fQjVxOvwHR_5I4)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | +Adapted from "[Soil Moisture Tester](https://drive.google.com/open?id=1Rv4oPoxrggbokczbroQUl-10py3_5fQjVxOvwHR_5I4)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) diff --git a/docs/courses/ucp-science/soil-moisture/setup-procedure.md b/docs/courses/ucp-science/soil-moisture/setup-procedure.md index 30fbfcc0531..c85906f30f5 100644 --- a/docs/courses/ucp-science/soil-moisture/setup-procedure.md +++ b/docs/courses/ucp-science/soil-moisture/setup-procedure.md @@ -66,11 +66,13 @@ input.onButtonPressed(Button.A, () => { 5. Share the project by clicking the **Share** button at the top of the editor window. 6. Report on the findings from the experiment. -## ~hint +### ~hint + +#### Soil Moisture Project This experiment is modified from the [Soil Moisture](https://makecode.microbit.org/projects/soil-moisture) project. -## ~ +### ~ ## Data Collection @@ -95,8 +97,9 @@ Display (“DRY”) Add code that would sound an alarm (play music) if the soil dries out below a certain point. The microbit could be left in a pot of soil with plants. It could be programmed to only sample the moisture every hour or so and then play the alarm when it is dry. This would help conserve the battery on the microbit. -
+## ~button /courses/ucp-science/soil-moisture/resources +NEXT: Resources +## ~ -| | | | -|-|-|-| -| Adapted from "[Soil Moisture Tester](https://drive.google.com/open?id=1Rv4oPoxrggbokczbroQUl-10py3_5fQjVxOvwHR_5I4)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | +
+Adapted from "[Soil Moisture Tester](https://drive.google.com/open?id=1Rv4oPoxrggbokczbroQUl-10py3_5fQjVxOvwHR_5I4)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) diff --git a/docs/courses/ucp-science/spoon-race.md b/docs/courses/ucp-science/spoon-race.md new file mode 100644 index 00000000000..fd023a61ee3 --- /dev/null +++ b/docs/courses/ucp-science/spoon-race.md @@ -0,0 +1,16 @@ +# Spoon Race + +The egg-and-spoon race was first invented in 1894 in England where it was part of local village celebrations and picnics (alongside tug-of-war and wheelbarrow races). Competitors race with an uncooked egg balanced on a spoon. The objective is to get the finish line first without dropping the egg. + +![Runners in the 1920 Egg and Spoon Race](/static/courses/ucp-science/spoon-race/egg-and-spoon-race-1920.jpg) + +In this lesson, students will use the micro:bit in place of an egg, and collect data on the movement of the micro:bit during the race to determine a winner. + +## Contents + +* [Overview](/courses/ucp-science/spoon-race/overview) +* [Setup and procedure](/courses/ucp-science/spoon-race/setup-procedure) +* [Resources](/courses/ucp-science/spoon-race/resources) + +
+Contributed by the [Utah Coding Project](https://sites.google.com/view/utahcodingproject) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) \ No newline at end of file diff --git a/docs/courses/ucp-science/spoon-race/overview.md b/docs/courses/ucp-science/spoon-race/overview.md new file mode 100644 index 00000000000..49e4497b9eb --- /dev/null +++ b/docs/courses/ucp-science/spoon-race/overview.md @@ -0,0 +1,57 @@ +# Overview + +## Science concept + +The micro:bit can measure movement through an accelerometer located on the back of the micro:bit - allowing it to detect if you shake, tilt or move the micro:bit in any direction. Many modern electronic devices have accelerometers built-in - including mobile phones, game controllers, and car sensors. + +![micro:bit accelerometer](/static/courses/ucp-science/spoon-race/microbit-accelerometer.png) + +Acceleration is the change in speed over a given amount of time - for example, it may take a sports car 3 seconds to accelerate from 0 to 60 miles-per-hour. The micro:bit can measure acceleration in any direction using a capacitor which stores and measures electrical energy. + +The micro:bit measures Acceleration along 3 dimensions: + +* X-axis (tilting left and right) +* Y-axis (tilting forward and backward) +* Z-axis (moving up and down) + +![micro:bit axis diagram](/static/courses/ucp-science/spoon-race/microbit-axis.png) + +The acceleration is measured in MakeCode with values between -1023 and +1023. When the micro:bit is laying flat: + +* X = 0 +* Y = 0 +* Z = -1023 + +![Simulator with XYZ forces](/static/courses/ucp-science/spoon-race/simulator-xyz-accel.png) + +Move your mouse cursor over the micro:bit simulator to see how the X, Y and Z values change. + +## Project Goal + +Provide a fun way for students to learn about motion sensing, data collection and analysis using MakeCode’s block programming and a micro:bit with sensors. + +## Prior Knowledge + +Students need to have a basic knowledge of how to code using block style programming and download a program to a micro:bit using MakeCode. + +## Student Outcomes + +The objective of the spoon race is to carry an object quickly but with minimal movement. Students will measure the amount of movement of their micro:bits during the race. The student with the fastest time and the least amount of movement wins the race. + +Students will: + +* Understand what acceleration is, and how accelerometers are used to measure motion. +* Code a micro:bit to measure acceleration values. +* Run a micro:bit spoon race to collect data. +* Analyze the results of the data to determine a winner. + +## Materials Needed + +* A micro:bit v2, micro-USB cable and battery pack +* A computer with internet access +* A spoon +* Optional - tape or empty plastic egg shell to enclose micro:bit during race + +## ~button /courses/ucp-science/spoon-race/setup-procedure +NEXT: Setup an Procedure +## ~ \ No newline at end of file diff --git a/docs/courses/ucp-science/spoon-race/resources.md b/docs/courses/ucp-science/spoon-race/resources.md new file mode 100644 index 00000000000..d2188edb559 --- /dev/null +++ b/docs/courses/ucp-science/spoon-race/resources.md @@ -0,0 +1,24 @@ +# Resources + +## CSTA Standards + +https://csteachers.org/k12standards + +### Computing Systems + +* 02 - Design projects that combine hardware and software components to collect and exchange data. +* 03 - Systematically identify and fix problems with computing devices and their components. + +### Data & Analysis + +* 07 - Represent data using multiple encoding schemes. +* 08 - Collect data using computational tools and transform the data to make it more useful and reliable. +* 09 - Refine computational models based on the data they have generated. + +## Other Resources + +* Micro:bit Accelerometer Overview – https://youtu.be/UT35ODxvmS0 +* Behind the MakeCode Hardware: Accelerometer – https://youtu.be/byngcwjO51U +* Microbit.org Classroom Resources – https://microbit.org/teach/classroom-resources +* MakeCode Reference Documentation – https://makecode.microbit.org/reference +* Utah Coding Project - https://sites.google.com/view/utahcodingproject \ No newline at end of file diff --git a/docs/courses/ucp-science/spoon-race/setup-procedure.md b/docs/courses/ucp-science/spoon-race/setup-procedure.md new file mode 100644 index 00000000000..5c989eb2300 --- /dev/null +++ b/docs/courses/ucp-science/spoon-race/setup-procedure.md @@ -0,0 +1,232 @@ +# Setup and procedure + +## Setup + +* Review the definition of acceleration and discuss the different uses of accelerometers in every-day devices. +* Explore how the micro:bit measures motion along the X, Y and Z axis using the ``||input:acceleration||`` block in MakeCode. +* Code the micro:bit to collect and store acceleration values using the data logger blocks. +* Create a space to race with a start and finish line. +* Group students to race in heats. +* Download the data from the micro:bits and analyze the results. +* Compare student scores to see who the winner is. +* Debrief on the results. + +## Code + +This project will use the Data Logger extension which only works on the micro:bit v2. + +* Create a new project. +* Click on **Extensions** in the Toolbox and add the Data Logger Extension. + +![Select Datalogger extension](/static/courses/ucp-science/spoon-race/extension.png) + +**Note**: The Data Logger extension allows you to collect data from the micro:bit sensors and store in a file on the micro:bit device. Because of this, it is good for long-running experiments, or experiments where the micro:bit is away from the computer. The micro:bit v2 has 512 KB of flash memory, so be careful of how much data you collect - once you reach the limit, you won’t be able to collect any more! + +* Log data every 1 second - from the ``||loops:Loops||`` category, drag an ``||loops:every 500ms||`` block out on the workspace. +* Click on the drop-down menu to change to 1 second (1000 milliseconds). + +```blocks +loops.everyInterval(1000, function () { + +}) +``` + +* From the ``||datalogger:Data Logger||`` category, drag a ``||datalogger:log data||`` block into the ``||loops:every 1000ms||`` block. +* In the ``||datalogger:log data||`` block, click on the plus (+) icon twice to add 2 more data fields to the log. +* Name these columns: "AX", "AY" and "AZ" to represent the three axis of motion (alternately you can name them more descriptive names like "TiltLeftRight", "TiltForwardBack" or "MoveUpDown"). + +```blocks +loops.everyInterval(1000, function () { + datalogger.log( + datalogger.createCV("AX", 0), + datalogger.createCV("AY", 0), + datalogger.createCV("AZ", 0) + ) +}) +``` + +* From the ``||input:Input||`` category, drag three ``||input:acceleration||`` blocks into the ``||datalogger:log data||`` block, replacing the values of 0. +* Using the drop-down menus, change the second and third ``||input:acceleration||`` blocks to y and z. + +```blocks +loops.everyInterval(1000, function () { + datalogger.log( + datalogger.createCV("AX", input.acceleration(Dimension.X)), + datalogger.createCV("AY", input.acceleration(Dimension.Y)), + datalogger.createCV("AZ", input.acceleration(Dimension.Z)) + ) +}) +``` + +Notice in the micro:bit simulator, you can start to see simulated data. Click the **Show data** Simulator button. Try moving your mouse cursor over the on-screen micro:bit to simulate movement and see how the accelerometer values that are logged every 1 second change. Click on the Go back button to return to the editor. + +![Simulator with logged data](/static/courses/ucp-science/spoon-race/simulator.png) + +Now that we are collecting accelerometer data, we need a way to start and stop data collection. To do this, we will use a flag. A flag is a Boolean variable - meaning, that it can only hold true or false values. You can think of a flag as a light switch - it is either on or off. Software developers often use feature flags to enable or disable certain features in a product. + +* From the ``||input:Input||`` category, drag two ``||input:on button pressed||`` blocks onto the workspace. +* In one of the ``||input:on button pressed||`` blocks, click on the drop-down menu to change to button B. + +```blocks +input.onButtonPressed(Button.A, function () { + +}) +input.onButtonPressed(Button.B, function () { + +}) +``` + +* In the ``||variables:Variables||`` category, click on the **Make a Variable** button. +* Name this variable "IsLogging" and press Ok. +* From the ``||variables:Variables||`` category, drag two ``||variables:Set IsLogging||`` blocks and drop one each into the ``||input:on button A pressed||`` and ``||input:on button B pressed||`` blocks. +* From the ``||logic:Logic||`` category, drag a ``||logic:true||`` block into the Button A ``||variables:Set IsLogging||`` block replacing the 0. +* From the ``||logic:Logic||`` category, drag a ``||logic:false||`` block into the Button B ``||variables:Set IsLogging||`` block replacing the 0. + +```blocks +let IsLogging = false +input.onButtonPressed(Button.A, function () { + IsLogging = true +}) +input.onButtonPressed(Button.B, function () { + IsLogging = false +}) +``` + +* From the ``||logic:Logic||`` category, drag a ``||logic:if true then||`` block out into the ``||loops:every 1000ms||`` block to surround the ``||datalogger:log data||`` block. +* From the ``||variables:Variables||`` category, drag a ``||variables:IsLogging||`` block into the ``||logic:if true then||`` block replacing ``||logic:true||``. + +```blocks +let IsLogging = false +loops.everyInterval(1000, function () { + if (IsLogging) { + datalogger.log( + datalogger.createCV("AX", input.acceleration(Dimension.X)), + datalogger.createCV("AY", input.acceleration(Dimension.Y)), + datalogger.createCV("AZ", input.acceleration(Dimension.Z)) + ) + } +}) +``` + +Lastly, let’s add a visual indicator that our micro:bit is logging data. + +* From the ``||basic:Basic||`` category, drag a ``||basic:show icon||`` block into the ``||logic:if true then||`` block just above the ``||datalogger:log data||`` block. +* Using the icon drop-down menu select an image that represents data logging to you. +* From the ``||basic:Basic||`` category, drag a ``||basic:clear screen||`` block and drop after the ``||basic:show icon||`` block. + +Note that the default behavior of the data logger is to append data to the data log file until you download a new program to the micro:bit. If you would like to wipe all previous data from the data log file, you can add a ``||datalogger:delete log||`` block. This will delete all existing data from the log each time you press button A to start the data collection. + +```blocks +let IsLogging = false +input.onButtonPressed(Button.A, function () { + datalogger.deleteLog() + IsLogging = true +}) +``` + +Your complete code should look something like this: + +```blocks +let IsLogging = false +input.onButtonPressed(Button.A, function () { + datalogger.deleteLog() + IsLogging = true +}) +input.onButtonPressed(Button.B, function () { + IsLogging = false +}) +loops.everyInterval(1000, function () { + if (IsLogging) { + basic.showIcon(IconNames.SmallDiamond) + basic.clearScreen() + datalogger.log( + datalogger.createCV("AX", input.acceleration(Dimension.X)), + datalogger.createCV("AY", input.acceleration(Dimension.Y)), + datalogger.createCV("AZ", input.acceleration(Dimension.Z)) + ) + } +}) +``` + +Sample code file: https://makecode.microbit.org/_Ur50YwCAxeEf + +Try it out in the simulator by pressing the A button to start collecting data, and press the B button to stop data collection. + +![Data collection run in simulator](/static/courses/ucp-science/spoon-race/sim-data.gif) + +Download the code onto the micro:bit, and then connect the micro:bit to a battery pack. + +## Let’s Race! + +After coding the micro:bit, assemble it together on a spoon. You can either place it by itself, or construct some sort of carrier to make it easier to carry with a spoon. Just be sure you can access the A and B buttons. + +A micro:bit paper holder that comes with the Go Kit: + +![Spoon with a micro:bit and paper holder](/static/courses/ucp-science/spoon-race/spoon-1.jpg) + +Affixing the micro:bit to an egg: + +![Spoon with egg attached to micro:bit](/static/courses/ucp-science/spoon-race/spoon-2.jpg) + +Carrying it loose: + +![Spoon with just a micro:bit](/static/courses/ucp-science/spoon-race/spoon-3.jpg) + +When you are ready to race, press button A to start the race, and press button B to end the race. Do not press the buttons again until you can download your data from the micro:bit. + +## Analyze the Data + +After the race, plug your micro:bit back into a computer using the USB cable. Use the file explorer, navigate to the MICROBIT drive, and double click the **MY_DATA.htm** file. + +![Finding the MY_DATA.HTM file](/static/courses/ucp-science/spoon-race/my-data-htm.png) + +You will see your data from the race in a table format. + +![Viewing the MY_DATA.HTM file as a table](/static/courses/ucp-science/spoon-race/my-data-table.png) + +You can click to see a visual preview of the data as well. + +![Viewing the MY_DATA.HTM file as a graph](/static/courses/ucp-science/spoon-race/my-data-graph.png) + +Click the Download button to download your data to a **microbit.csv** file, then open it with Excel. Create a line chart for the data table with time on the X axis. + +![Spreadsheet with line chart on x-axis](/static/courses/ucp-science/spoon-race/spreadsheet-1.png) + +Perform the following calculations: + +* Calculate Time Elapsed: End_Time – Start_Time + +![Spreadsheet of elapsed time](/static/courses/ucp-science/spoon-race/spreadsheet-2.png) + +* Calculate the difference between the acceleration values for AX, AY and AZ columns: Maximum_Value – Minimum_Value + +![Spreadsheet of max/min difference of acceleration](/static/courses/ucp-science/spoon-race/spreadsheet-3.png) + +* Calculate the average of the three axis of movement: Average(AX, AY, AZ) + +![Spreadsheet of average of 3-axis movement](/static/courses/ucp-science/spoon-race/spreadsheet-4.png) + +* Compare each student’s time and average spread of motion results in a table. The student with the shortest time, and the lowest average acceleration value spread wins the race! + +![Spreadsheet of time and average variance](/static/courses/ucp-science/spoon-race/spreadsheet-5.png) + + +See sample result data: https://aka.ms/SpoonRaceExcel + +## Debrief + +Discuss the results of the experiment: + +* What was the pattern for the most successful racers? +* Were there instances where the data collection failed? +* Were there outlier data points in any of the races? +* What could that indicate? +* Are there any other ways you might calculate the winning criteria? + +## ~button /courses/ucp-science/spoon-race/resources +NEXT: Resources +## ~ + +```package +datalogger +``` diff --git a/docs/courses/ucp-science/temperature.md b/docs/courses/ucp-science/temperature.md index 3de3af3a5b7..a7a47c210b2 100644 --- a/docs/courses/ucp-science/temperature.md +++ b/docs/courses/ucp-science/temperature.md @@ -6,17 +6,13 @@ This lesson give students real world experience with coding, collecting temperat ## Lesson concept -### ~ hint - -#### Direct sunlight and shade experiment +### Direct sunlight and shade experiment Watch this video about an experiment using the temperature sensor to see what effect a sun shade has on keeping your car cooler. https://youtu.be/pHDYsy6xyE4 -### ~ - ## Contents * [Overview](/courses/ucp-science/temperature/overview) @@ -24,7 +20,4 @@ https://youtu.be/pHDYsy6xyE4 * [Resources](/courses/ucp-science/temperature/resources)
- -| | | | -|-|-|-| -| Adapted from "[Temperature Data](https://drive.google.com/open?id=1X6FeANka2qcMC2ZFQgSSxEoHxsQc--6a0Pk9xxMOwE8)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | +Adapted from "[Temperature Data](https://drive.google.com/open?id=1X6FeANka2qcMC2ZFQgSSxEoHxsQc--6a0Pk9xxMOwE8)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) diff --git a/docs/courses/ucp-science/temperature/overview.md b/docs/courses/ucp-science/temperature/overview.md index 7f37e410783..1bbce000e84 100644 --- a/docs/courses/ucp-science/temperature/overview.md +++ b/docs/courses/ucp-science/temperature/overview.md @@ -43,8 +43,9 @@ Students will: * A longer USB microbit cable * Spreadsheet for data analysis and a word processor for reporting the findings -
+## ~button /courses/ucp-science/temperature/setup-procedure +NEXT: Setup an Procedure +## ~ -| | | | -|-|-|-| -| Adapted from "[Temperature Data](https://drive.google.com/open?id=1X6FeANka2qcMC2ZFQgSSxEoHxsQc--6a0Pk9xxMOwE8)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | \ No newline at end of file +
+Adapted from "[Temperature Data](https://drive.google.com/open?id=1X6FeANka2qcMC2ZFQgSSxEoHxsQc--6a0Pk9xxMOwE8)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) \ No newline at end of file diff --git a/docs/courses/ucp-science/temperature/resources.md b/docs/courses/ucp-science/temperature/resources.md index 5c8af77f3f7..e53a0192c99 100644 --- a/docs/courses/ucp-science/temperature/resources.md +++ b/docs/courses/ucp-science/temperature/resources.md @@ -67,8 +67,5 @@ All Earth processes are the result of energy flowing and matter cycling within a * [Blog entry on Windows 10 MakeCode app](https://sites.google.com/view/utahcodingproject/blog/2018-jan-makecode-app)
- -| | | | -|-|-|-| -| Adapted from "[Temperature Data](https://drive.google.com/open?id=1X6FeANka2qcMC2ZFQgSSxEoHxsQc--6a0Pk9xxMOwE8)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | +Adapted from "[Temperature Data](https://drive.google.com/open?id=1X6FeANka2qcMC2ZFQgSSxEoHxsQc--6a0Pk9xxMOwE8)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) diff --git a/docs/courses/ucp-science/temperature/setup-procedure.md b/docs/courses/ucp-science/temperature/setup-procedure.md index 2eefcfba86b..0a3aadeebee 100644 --- a/docs/courses/ucp-science/temperature/setup-procedure.md +++ b/docs/courses/ucp-science/temperature/setup-procedure.md @@ -154,11 +154,12 @@ Use a radio connection to collect and record the outside temperature. It could b Several students could use micorbits to observe the temperature at different elevations where the live at set times to see if there are patterns in temperatures at different elevations or regions. -
+## ~button /courses/ucp-science/temperature/resources +NEXT: Resources +## ~ -| | | | -|-|-|-| -| Adapted from "[Temperature Data](https://drive.google.com/open?id=1X6FeANka2qcMC2ZFQgSSxEoHxsQc--6a0Pk9xxMOwE8)" by [C Lyman](http://utahcoding.org) | | [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) | +
+Adapted from "[Temperature Data](https://drive.google.com/open?id=1X6FeANka2qcMC2ZFQgSSxEoHxsQc--6a0Pk9xxMOwE8)" by [C Lyman](http://utahcoding.org) [![CC BY-NC-SA](https://licensebuttons.net/l/by-nc-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-nc-sa/4.0/) ```package radio diff --git a/docs/device.md b/docs/device.md index 6ad5845772e..d106978aae6 100644 --- a/docs/device.md +++ b/docs/device.md @@ -1,16 +1,24 @@ # Device +### ~ hint -## ~ hint +#### Looking to buy a micro:bit? -**Looking to buy a micro:bit?** See the [list of resellers](https://microbit.org/resellers). +See the [list of official products](https://microbit.org/buy/). -## ~ +### ~ All the bits and pieces that make up the BBC micro:bit -![micro:bit board layout](/static/mb/device-0.png) +![micro:bit board layout](/static/mb/device-v2.jpg) +### ~ hint + +#### The current version of micro:bit is v2 + +The version of @boardname@ is now currently at **v2**. See the what's new in MakeCode for programming the [@boardname@ v2](/device/v2). + +### ~ ## LED Screen and Status LED @@ -27,17 +35,21 @@ https://www.youtube.com/watch?v=qqBmvHD5bCw ## Buttons -Buttons A and B are a form of input. When you press a button, it completes an electrical circuit. +Buttons **A** and **B** are a form of input. When you press a button, it completes an electrical circuit. The micro:bit can detect either of its two buttons being pressed/released and be programmed to act on these events. -Button R on the back of the micro:bit is a system button. It has different uses. -When you have downloaded and run your code onto your micro:bit, press Button R to restart and run your program from the beginning. +Button **R** on the back of the micro:bit is a system button. It has different uses. +When you have downloaded and run your code onto your micro:bit, press Button **R** to restart and run your program from the beginning. Find out how buttons provide input to the @boardname@ in this video: https://www.youtube.com/watch?v=t_Qujjd_38o +## Touch + +Pins **0**, **1**, **2**, and the board **logo** can work as touch buttons when they are programmed for input. + ## USB connection When you plug in your micro:bit via [USB](/device/usb), it should appear as a ``MICROBIT`` drive. @@ -47,9 +59,9 @@ the micro:bit will appear as a ``MAINTENANCE`` drive instead of ``MICROBIT``. Th To continue programming your micro:bit YOU MUST unplug your USB and reconnect it. Check that the drive now shows as ``MICROBIT``. -## ~ hint +### ~ hint -### Open the version file +#### Open the version file Use with caution! If you click on the drive while it shows the ``MAINTENANCE`` label, @@ -57,7 +69,7 @@ you can see which version of firmware you have running on your micro:bit. Firmware on your micro:bit should be up-to-date already. You can find the version of firmware in the 'version.txt' file on the micro:bit. See the @boardname@ **[firmware](https://microbit.org/guide/firmware/)** page for more about checking your board's firmware version. -## ~ +### ~ ## Compass @@ -79,7 +91,11 @@ https://www.youtube.com/watch?v=byngcwjO51U ## Pins The [pins](/device/pins) can be a form of electrical input or output. -There are labels for the input/output pins ``P0``, ``P1``, ``P2``, which you can attach external sensors to such as thermometers or moisture detectors. +There are labels for the input/output pins **0**, **1**, **2**, which you can attach external sensors to such as thermometers or moisture detectors. + +## Microphone + +Using the microphone, your programs can detect sounds that are present. You can check for loud or quiet sounds and find out what their sound level is. ## Light level @@ -91,7 +107,7 @@ https://www.youtube.com/watch?v=TKhCr-dQMBY ## Temperature -Temperatrue is measured on the @boardname@ by detecting how hot its physical CPU material is. Since it operates nearly as cool as the air around it, the temperature it measures for itself is a good approximation for the ambient temperature (the temperature near and around it). +Temperature is measured on the @boardname@ by detecting how hot its physical CPU material is. Since it operates nearly as cool as the air around it, the temperature it measures for itself is a good approximation for the ambient temperature (the temperature near and around it). See how the @boardname@ can detect hot or cold in this temperature sensing video: @@ -119,7 +135,7 @@ and [click here to read more about the error messages you might get](/device/err When your micro:bit is connected to your computer with the micro USB, it doesn’t need another power source. When your micro:bit isn’t connected to your computer, tablet or mobile, you will need 2 x AAA 1.5 V batteries to power it. -The pins labelled 3V and GND are the power supply pins. +The pins labelled **3V** and **GND** are the power supply pins. You can attach an external device such as a motor to these and power it using the battery or USB. ## Serial Communication diff --git a/docs/device/error-codes.md b/docs/device/error-codes.md index 32e074dc2c8..253e217d047 100644 --- a/docs/device/error-codes.md +++ b/docs/device/error-codes.md @@ -1,6 +1,6 @@ # Error codes -Your @boardname@ may encounter a situation that prevents it from running your code. When this happens, a frowny face will appear on your @boardname@ screen (see picture) followed by an error number. These are called _panic_ codes. +Your @boardname@ may encounter a situation that prevents it from running your code. When this happens, a frowny face will appear on your @boardname@ screen (see picture) followed by an error number. These are called _panic_ codes. ```sim basic.forever(function() { @@ -67,6 +67,7 @@ Error codes generated from the garbage collector. * **907** (`PANIC_NO_SUCH_CONFIG`): the specified device resource is not present * **909** (`PANIC_INVALID_ARGUMENT`): the argument value is out of range or the type or format is invalid * **927** (`PANIC_VARIANT_NOT_SUPPORTED`): using a v2 feature on a v1 board +* **928** (`MICROBIT_LOG_FULL`): The @boardname@ failed to write to datalogger as the log was full ## JavaScript runtime codes diff --git a/docs/device/incompatible.md b/docs/device/incompatible.md new file mode 100644 index 00000000000..98f074ab591 --- /dev/null +++ b/docs/device/incompatible.md @@ -0,0 +1,13 @@ +# Incompatibile Hardware + +A newer version of @boardname@ usually adds hardware features which also bring new support from MakeCode to let you use them in your programs. This might be new blocks (or API's), and parameters to let code your programs for these new features. + +If you have a program that's coded to work with the hardware introduced by the newer version of @boardname@, it will work with that board and most likely any newer future version that keeps these features. If you try to download this this program to a previous version of @boardname@ though, you will probably receive an error message telling you that your program contains - **Incompatible Code**. + +You can download a program to a version of @boardname@ that doesn't have the hardware to support the all of the blocks (or API's) you've included in your code. Your program may run fine if it doesn't reach any code that uses any of the incompatible hardware features. If your program does try to run code that is incompatible, a hardware error will occur and your program will stop. An error code may show on the screen and you will need to reset the @boardname@ to run the program again. + +## Hardware support levels + +You can see if your @boardname@ will work with all of the code in your program by checking its hardware version page for the features it supports: + +* [micro:bit v2](/device/v2) diff --git a/docs/device/pins.md b/docs/device/pins.md index 14a97f7d916..dabdf4fe62f 100644 --- a/docs/device/pins.md +++ b/docs/device/pins.md @@ -1,64 +1,96 @@ # micro:bit pins -The micro:bit pins +The micro:bit has **25** external connections on the edge connector of the board, which are referred to as 'pins'. The edge connector is the gold area on the right side of board as shown the figure below. -![](/static/mb/device/pins-0.png) +![micro:bit v1 pins](/static/mb/device/pins-v1-v2.png) -The micro:bit has 25 external connections on the edge connector of the board, which we refer to as ‘pins’. The edge connector is the grey area on the right side of the figure above. - -There are five large pins, that are also connected to holes in the board labelled: 0, 1, 2, 3V, and GND. And along the same edge, there are 20 small pins that you can use when plugging the micro:bit into an edge connector. +There are **5 large pins** that are also connected to holes in the board labelled: **0**, **1**, **2**, **3V**, and **GND**. And along the same edge, there are **20 small pins** that you can use when plugging the micro:bit into an edge connector. ## Large pins -You can easily attach crocodile clips or 4mm banana plugs to the five large pins. +You can easily attach crocodile clips or 4mm banana plugs to the **5** large pins. -The first three, labelled 0, 1 and 2 are flexible and can be used for many different things - which means they are often called ‘general purpose input and output’ (shortened to GPIO). These three pins also have the ability to read analogue voltages using something called an analogue-to-digital converter (ADC). They all have the same function: +The first three, labelled **0**, **1** and **2** are flexible and can be used for many different things - which means they are often called "general purpose input and output" (shortened to GPIO). These three pins also have the ability to read analog voltages using something called an analog-to-digital converter (ADC). They all have the same function: -* **0**: GPIO (general purpose digital input and output) with analogue to digital convertor (ADC). +* **0**: GPIO (general purpose digital input and output) with analog-to-digital convertor (ADC). * **1**: GPIO with ADC * **2**: GPIO with ADC -The other two large pins (3V and GND) are very different! +With the micro:bit V2, pins **0**, **1**, **2**, and the **LOGO** can also be set to work as [capacitive touch](/reference/pins/touch-set-mode) buttons. + +### Power pins + +The other two large pins (**3V** and **GND**) are very different! -## ~hint +### ~hint -Watch out! The pins labelled 3V and GND relate to the power supply of the board, and they should NEVER be connected together. -For details on the power, current and voltage limitations of the board, see [Power Supply](https://tech.microbit.org/hardware/powersupply/) +#### Be careful with the power pins -## ~ +Watch out! The pins labelled **3V** and **GND** relate to the power supply of the board, and they should NEVER be connected together. -*power input*: If the micro:bit is powered by USB or a battery, then you can use the 3V pin as a *power output* to power peripherals with. +For details on the power, current and voltage limitations of the board, see [Power Supply](https://tech.microbit.org/hardware/powersupply/). -* **3V**: *3 volt power output* or *power input*. (1) *power output*: If the micro:bit is powered by USB or a battery, then you can use the 3V pin as a power output to power peripherals with; (2) *power input*: If the micro:bit is not being powered by USB or battery, you can use the 3V pin as a power input to power the micro:bit -* **GND**: attaches to ground in order to complete a circuit (required when using the 3V pin) +### ~ -If you hold the ‘GND’ pin with one hand, you can program the microbit to detect yourself touching the 0,1 or 2 pins with your other hand, giving you three more buttons to experiment with (you just used your body to complete an electrical circuit). + +* **3V**: 3 volt *power output* or *power input*: +>* *power output*: If the micro:bit is powered by USB or a battery, then you can use the **3V** pin as a power output to power peripherals with. +>* *power input*: If the micro:bit is NOT being powered by USB or battery, you can use the **3V** pin to supply power input to the micro:bit. +* **GND**: attaches to ground in order to complete a circuit (required when using the **3V** pin) + +If you hold the **GND** pin with one hand, you can program the microbit to detect yourself touching the **0**, **1** or **2** pins with your other hand, giving you three more buttons to experiment with (you just used your body to complete an electrical circuit to make "resistive touch" buttons). ## Small pins -There are 20 small pins numbered sequentially from 3-22 (these pins are not labeled on the micro:bit, however, they are labelled in the picture above). - -Unlike the three large pins that are dedicated to being used for external connections, some of the small pins are shared with other components on the micro:bit board. For example, pin 3 is shared with some of the LEDs on the screen of the micro:bit, so if you are using the screen to scroll messages, you can’t use this pin as well. - -* **pin 3**: GPIO shared with LED Col 1 of the LED screen; can be used for ADC and digital I/O when the LED screen is turned off. -* **pin 4**: GPIO shared with LED Col 2 of the LED screen; can be used for ADC and digital I/O when the LED screen is turned off. -* **pin 5**: GPIO shared with Button A. This lets you trigger or detect a button "A" click externally. This pin has a pull-up resistor, which means that by default it is at voltage of 3V. To replace button A on the micro:bit with an external button, connect one end of the external button to pin 5 and the other end to GND. When the button is pressed, the voltage on pin 5 is pulled down to 0, which generates a button click event. -* **pin 6**: GPIO shared with LED Col 9 of the LED screen; can be used for digital I/O when the LED screen is turned off. -* **pin 7**: GPIO shared with LED Col 8 of the LED screen; can be used for digital I/O when the LED screen is turned off. -* **pin 8**: Dedicated GPIO, for sending and sensing digital signals. -* **pin 9**: GPIO shared with LED Col 7 of the LED screen; can be used for digital I/O when the LED screen is turned off. -* **pin 10**: GPIO shared with LED Col 3 of the LED screen; can be used for ADC and digital I/O when the LED screen is turned off. -* **pin 11**: GPIO shared with Button B. This lets you trigger or detect a button “B” click externally. -* **pin 12**: this GPIO pin has been reserved to provide support for accessibility. -* **pin 13**: GPIO that is conventionally used for the serial clock (SCK) signal of the 3-wire Serial Peripheral Interface (SPI) bus. -* **pin 14**: GPIO that is conventionally used for the Master In Slave Out (MISO) signal of the SPI bus. -* **pin 15**: GPIO that is conventionally used for the Master Out Slave In (MOSI) signal of the SPI bus. -* **pin 16**: Dedicated GPIO (conventionally also used for SPI ‘Chip Select’ function). -* **pins 17 and 18**: these pins are wired to the 3V supply, like the large ‘3V’ pad. -* **pins 19 and 20**: implement the clock signal (SCL) and data line (SDA) of the I2C bus communication protocol. With I2C, several devices can be connected on the same bus and send/read messages to and from the CPU. Internally, the accelerometer and the compass are connected to i2c. -* **pins 21 and 22**: these pins are wired to the GND pin and serve no other function +The **20** small pins are numbered sequentially from **3-22** (these pins are not labeled on the micro:bit, however, they are labelled in the picture above). + +Unlike the three large pins that are dedicated to being used for external connections, some of the small pins are shared with other components on the micro:bit board. For example, pin **3** is shared with some of the LEDs on the screen of the micro:bit, so if you are using the screen to scroll messages, you can’t use this pin as well. + +There are some differences in function assignments for the small pins between the micro:bit versions. The following pin tables describe the pin functions for each version. + +### V1 pin map + +| Pin | Description | +| - | - | +| **3** | GPIO shared with LED Col 1 of the LED screen; can be used for ADC and digital I/O when the LED screen is turned off. | +| **4** | GPIO shared with LED Col 2 of the LED screen; can be used for ADC and digital I/O when the LED screen is turned off. | +| **5** | GPIO shared with Button A. This lets you trigger or detect a button "A" click externally. This pin has a pull-up resistor, which means that by default it is at voltage of 3V. To replace button A on the micro:bit with an external button, connect one end of the external button to pin 5 and the other end to GND. When the button is pressed, the voltage on pin 5 is pulled down to 0, which generates a button click event. | +| **6** | GPIO shared with LED Col 9 of the LED screen; can be used for digital I/O when the LED screen is turned off. | +| **7** | GPIO shared with LED Col 8 of the LED screen; can be used for digital I/O when the LED screen is turned off. | +| **8** | Dedicated GPIO, for sending and sensing digital signals. +| **9** | GPIO shared with LED Col 7 of the LED screen; can be used for digital I/O when the LED screen is turned off. | +| **10** | GPIO shared with LED Col 3 of the LED screen; can be used for ADC and digital I/O when the LED screen is turned off.| +| **11** | GPIO shared with Button B. This lets you trigger or detect a button "B" click externally. | +| **12** | This GPIO pin has been reserved to provide support for accessibility. | +| **13** | GPIO that is conventionally used for the serial clock (SCK) signal of the 3-wire Serial Peripheral Interface (SPI) bus. | +| **14** | GPIO that is conventionally used for the Master In Slave Out (MISO) signal of the SPI bus. | +| **15** | GPIO that is conventionally used for the Master Out Slave In (MOSI) signal of the SPI bus. | +| **16** | Dedicated GPIO (conventionally also used for SPI 'Chip Select' function). | +| **17, 18** | These pins are wired to the 3V supply, like the large '3V' pad. | +| **19, 20** | Implement the clock signal (SCL) and data line (SDA) of the I2C bus communication protocol. With I2C, several devices can be connected on the same bus and send/read messages to and from the CPU. Internally, the accelerometer and the compass are connected to i2c. | +| **21, 22** | These pins are wired to the GND pin and serve no other function. | + +### V2 pin map + +| Pin | Description | +| - | - | +| **3** | GPIO shared with LED Col 3 of the LED screen; can be used for ADC and digital I/O when the LED screen is turned off. | +| **4** | GPIO shared with LED Col 1 of the LED screen; can be used for ADC and digital I/O when the LED screen is turned off. | +| **5** | GPIO shared with Button A. This lets you trigger or detect a button "A" click externally. This pin has a pull-up resistor, which means that by default it is at voltage of 3V. To replace button A on the micro:bit with an external button, connect one end of the external button to pin 5 and the other end to GND. When the button is pressed, the voltage on pin 5 is pulled down to 0, which generates a button click event. | +| **6** | GPIO shared with LED Col 2 of the LED screen; can be used for digital I/O when the LED screen is turned off. | +| **7** | GPIO shared with LED Col 4 of the LED screen; can be used for digital I/O when the LED screen is turned off. | +| **8, 9** | Dedicated GPIO, for sending and sensing digital signals; can also be configured for NFC. | +| **10** | GPIO shared with LED Col 5 of the LED screen; can be used for ADC and digital I/O when the LED screen is turned off.| +| **11** | GPIO shared with Button B. This lets you trigger or detect a button "B" click externally. | +| **12** | This GPIO pin has been reserved to provide support for accessibility. | +| **13** | GPIO that is conventionally used for the serial clock (SCK) signal of the 3-wire Serial Peripheral Interface (SPI) bus. | +| **14** | GPIO that is conventionally used for the Master In Slave Out (MISO) signal of the SPI bus. | +| **15** | GPIO that is conventionally used for the Master Out Slave In (MOSI) signal of the SPI bus. | +| **16** | Dedicated GPIO (conventionally also used for SPI 'Chip Select' function). | +| **17, 18** | These pins are wired to the 3V supply, like the large '3V' pad. | +| **19, 20** | Implement the clock signal (SCL) and data line (SDA) of the I2C bus communication protocol. With I2C, several devices can be connected on the same bus and send/read messages to and from the CPU. Internally, the accelerometer and the compass are connected to i2c. | +| **21, 22** | These pins are wired to the GND pin and serve no other function. | ## Connecting to the small pins -It is recommended that an edge connector be acquired to connect to the small pins. More information on compatible edge connectors will be available later. - +It is recommended that an edge connector designed for the micro:bit be used for connections to the small pins. For available edge connectors, put "edge connectors for the micro:bit" into your internet search engine to find an accessory supplier. diff --git a/docs/device/reactive.md b/docs/device/reactive.md index 5f0c32b8a53..d457e5e7f0e 100644 --- a/docs/device/reactive.md +++ b/docs/device/reactive.md @@ -4,25 +4,27 @@ What sort of a *computing system* is the micro:bit? -## ~hint +### ~hint + +#### Types of computing systems There are different types of computing systems, to address different kinds of problems that arise in practice: *transaction processing systems* are used by banks to handle huge numbers of financial transactions by their customers; *distributed systems* make a set of networked computers appear as one big computer (like Google’s search engine); there are also *parallel systems*, such as graphic cards, which perform a huge number of primitive operations simultaneously, using a great number of small processing cores. -## ~ +### ~ -The micro:bit is a *reactive system* – it reacts continuously to external events, such as a person pressing the **A** button of the micro:bit or shaking the device. The reaction to an event may be to perform a computation, update variables, and change the display. After the device reacts to an event, it is ready to react to the next one. If this sounds like a computer game, that’s because most computer games are reactive systems too! +The micro:bit is a *reactive system* – it reacts continuously to external events, such as a person pressing the **A** button of the micro:bit or shaking the device. The reaction to an event may be to perform a computation, update variables, and change the display. After the device reacts to an event, it is ready to react to the next one. If this sounds like a computer game, that’s because most computer games are reactive systems too! ## Responsiveness -We want reactive systems to be responsive, which means to react in a timely manner to events. For example, when you play a computer game, it’s frustrating if you press a button to make a character jump, but it doesn’t immediately jump. A delay in reacting, or lack of responsiveness, can be the difference between life and death, both in the real and virtual worlds. +We want reactive systems to be responsive, which means to react in a timely manner to events. For example, when you play a computer game, it’s frustrating if you press a button to make a character jump, but it doesn’t immediately jump. A delay in reacting, or lack of responsiveness, can be the difference between life and death, both in the real and virtual worlds. -Let’s consider a simple example: you want to program your micro:bit to accurately count the number of times the **A** button has been pressed and continuously display the current count on the 5x5 [LED screen](/device/screen). Because the LED screen is small, we can only display one digit of a number at a time on it. The [show number](/reference/basic/show-number) function will scroll the digits of a number across the screen so you can read it. +Let’s consider a simple example: you want to program your micro:bit to accurately count the number of times the **A** button has been pressed and continuously display the current count on the 5x5 [LED screen](/device/screen). Because the LED screen is small, we can only display one digit of a number at a time on it. The [show number](/reference/basic/show-number) function will scroll the digits of a number across the screen so you can read it. -Let’s say that the current count is 42 and the number 42 is scrolling across the LED screen. This means there is some code executing to perform the scroll. So, what should happen if you press the **A** button during the scroll? It would be a bad idea to ignore the button press, so some code should record the occurrence of the button press. But we just said there already is code running in order to scroll the number 42! If we wait until the code scrolling the 42 has finished to look for a button press, we will miss the button press. We want to avoid this sort of unresponsiveness. +Let’s say that the current count is 42 and the number 42 is scrolling across the LED screen. This means there is some code executing to perform the scroll. So, what should happen if you press the **A** button during the scroll? It would be a bad idea to ignore the button press, so some code should record the occurrence of the button press. But we just said there already is code running in order to scroll the number 42! If we wait until the code scrolling the 42 has finished to look for a button press, we will miss the button press. We want to avoid this sort of unresponsiveness. ## Concurrency -To be responsive, a reactive system needs to be able to do several things at the same time (concurrently), just like you can. But the micro:bit only has one CPU for executing your program, which means it can only execute one program instruction at a time. It can, however, execute millions of instructions in a single second. This points the way to a solution. +To be responsive, a reactive system needs to be able to do several things at the same time (concurrently), just like you can. But the micro:bit only has one CPU for executing your program, which means it can only execute one program instruction at a time. It can, however, execute millions of instructions in a single second. This points the way to a solution. Think about how a motion picture projector works - it projects only 24 frames per second, yet this is good enough to provide the illusion of fluid motion on the screen. The micro:bit can execute millions of instructions per second, so it seems quite possible for the device to both to smoothly scroll the number 42 across the LED screen while looking for button presses and counting them. @@ -36,7 +38,7 @@ In order to be responsive, we would like to *interrupt* the execution of sequenc ![Execution sequence diagram: S1 and S2](/static/mb/device/reactive-0.png) -The result is that it takes sequence **S1** a little longer to complete, due to the interruptions to execute sequence **S2**, but we are checking often enough to detect a press of button **A** . When **S2** detects a press of button **A**, then the sequence **S3** can be executed before **S1** resumes: +The result is that it takes sequence **S1** a little longer to complete, due to the interruptions to execute sequence **S2**, but we are checking often enough to detect a press of button **A** . When **S2** detects a press of button **A**, then the sequence **S3** can be executed before **S1** resumes: ![Execution sequence diagram: S1 and S2 with interrupt and one S3 slice](/static/mb/device/reactive-1.png) @@ -44,11 +46,11 @@ As we’ll soon see, there are other choices for how the sequences can be ordere ## The micro:bit scheduler and queuing up subprograms -The micro:bit’s *scheduler* provides the capability to concurrently execute different code sequences, relieving us of a lot of low-level programming. In fact, scheduling is so useful that it is a part of every *operating system*! +The micro:bit’s *scheduler* provides the capability to concurrently execute different code sequences, relieving us of a lot of low-level programming. In fact, scheduling is so useful that it is a part of every *operating system*! The first job of the scheduler is to allow multiple *subprograms* to be queued up for later execution. For our purposes, a subprogram is just a statement or sequence of statements in the context of a larger program. Consider the program below for counting button presses. -```typescript +```typescript-ignore let count = 0 input.onButtonPressed(Button.A, () => { @@ -63,14 +65,14 @@ basic.forever(() => { The program above contains three statements that execute in order from top to bottom. The first statement initializes the global variable `count` to zero. -```typescript +```typescript-ignore // statement 1 let count = 0 ``` -The second statement informs the scheduler that on each and every event of the **A** button being pressed, a subprogram (called the event handler) should be queued for execution. The event handler code is contained within the braces `{...}`; it increments the global variable `count` by one. +The second statement informs the scheduler that on each and every event of the **A** button being pressed, a subprogram (called the event handler) should be queued for execution. The event handler code is contained within the braces `{...}`; it increments the global variable `count` by one. -```typescript +```typescript-ignore // statement 1 let count = 0 // statement 2 @@ -81,7 +83,7 @@ input.onButtonPressed(Button.A, () => { The third statement queues a `forever` loop for later execution by the scheduler; the body of this loop (also inside the braces `{...}`) displays the current value of global variable `count` on the LED screen. -```typescript +```typescript-ignore // statement 1 let count = 0 // statement 2 @@ -94,23 +96,23 @@ basic.forever(() => { }) ``` -There are no more statements after the execution of these three statements, but this is not the end of program execution! That’s because the program queued the `forever` loop for execution by the scheduler (and registered an event handler for presses of button A). +There are no more statements after the execution of these three statements, but this is not the end of program execution! That’s because the program queued the `forever` loop for execution by the scheduler (and registered an event handler for presses of button A). -The second job of the scheduler is to periodically interrupt execution to read (poll) the various inputs to the micro:bit (the buttons, pins, etc.) and fire off events (such as “button A pressed”). Recall that the firing of an event causes the event handler subprogram associated with that event to be queued for later execution. The scheduler uses a timer built into the micro:bit hardware to interrupt execution every 6 milliseconds and poll the inputs, which is more than fast enough to catch the quickest press of a button. +The second job of the scheduler is to periodically interrupt execution to read (poll) the various inputs to the micro:bit (the buttons, pins, etc.) and fire off events (such as "button A pressed"). Recall that the firing of an event causes the event handler subprogram associated with that event to be queued for later execution. The scheduler uses a timer built into the micro:bit hardware to interrupt execution every 6 milliseconds and poll the inputs, which is more than fast enough to catch the quickest press of a button. ## Cooperative passing of control How does the `forever` loop get to start execution? Furthermore, once the `forever` loop is running, how does any other subprogram (like the event handler that increments the count) ever get a chance to execute? -The answer is “cooperation” and “passing”. Think of a football team doing a drill – there is one ball and each footballer gets to dribble the ball for a certain number of touches, after which they pass to another footballer. A footballer who never passes prevents all other footballers from dribbling. A cooperative footballer always passes to some other footballer after taking a few touches. +The answer is "cooperation" and "passing". Think of a football team doing a drill – there is one ball and each footballer gets to dribble the ball for a certain number of touches, after which they pass to another footballer. A footballer who never passes prevents all other footballers from dribbling. A cooperative footballer always passes to some other footballer after taking a few touches. If you hadn’t guessed already, a footballer represents subprogram and dribbling the ball corresponds to that subprogram executing. Only one subprogram gets to execute at a time, as there is only one ball (processor). Footballer Alice passing the ball to footballer Bob corresponds to stopping execution of Alice’s subprogram (and remembering where it stopped) and starting/resuming execution of Bob’s subprogram. -We will call this “passing control of execution” rather than “passing the ball”. However, in the world of the micro:bit, the concurrently executing subprograms are not aware of each other, so they don’t actually pass control directly to one another. Rather they pass control of execution back to the scheduler and the scheduler determines the subprogram to pass control to next. The programmer inserts a call to the `pause` function to indicate a point in the subprogram where control of execution passes to the scheduler. Also, when a subprogram ends execution, control passes to the scheduler. +We will call this "passing control of execution" rather than "passing the ball". However, in the world of the micro:bit, the concurrently executing subprograms are not aware of each other, so they don’t actually pass control directly to one another. Rather they pass control of execution back to the scheduler and the scheduler determines the subprogram to pass control to next. The programmer inserts a call to the `pause` function to indicate a point in the subprogram where control of execution passes to the scheduler. Also, when a subprogram ends execution, control passes to the scheduler. Let’s take a look at the implementation of the `basic.forever` function to see an example of cooperative scheduling: -```typescript +```typescript-ignore function forever_(body: () => void) { control.inBackground(() => { while(true) { @@ -121,9 +123,17 @@ function forever_(body: () => void) { } ``` -The `forever` loop actually is a function that takes a subprogram (another function) as a parameter. The function uses the `control.inBackground` function of the micro:bit runtime to queue a `while true` loop for execution by the scheduler. The while loop has two statements. The first statement runs the subprogram represented by the `body` parameter. The second statement passes control to the scheduler (requesting to “sleep” for 20 milliseconds). +The `forever` loop actually is a function that takes a subprogram (another function) as a parameter. The function uses the `control.inBackground` function of the micro:bit runtime to queue a `while true` loop for execution by the scheduler. The while loop has two statements. The first statement runs the subprogram represented by the `body` parameter. The second statement passes control to the scheduler (requesting to "sleep" for 20 milliseconds). + +Though the `while true` loop will repeatedly execute the body subprogram, between each execution of the body it will permit the scheduler to execute other subprograms. If the while loop did not contain the call to `pause`, then once control passed into the while loop, it would never pass back to the scheduler and no other subprogram would be able to execute (unless the body subprogram contained a call to `pause` itself). + +### ~hint -Though the `while true` loop will repeatedly execute the body subprogram, between each execution of the body it will permit the scheduler to execute other subprograms. If the while loop did not contain the call to `pause`, then once control passed into the while loop, it would never pass back to the scheduler and no other subprogram would be able to execute (unless the body subprogram contained a call to `pause` itself). +#### Pauses within blocks + +Certain blocks may contain a `pause` within their code to allow execution control to return to the scheduler. As an example, when a device is interacting with the code in a block, control can return to the scheduler to allow other subprograms run while that device is taking time to respond. + +### ~ ## Round-robin scheduling @@ -141,20 +151,20 @@ Let’s go back to the `count button presses` program and revisit its execution 2. Set up the event handler for each press of button **A** 3. Queue the forever loop to the run queue -The program then ends execution and control passes back to the scheduler. Let’s assume the user has not pressed any buttons . The scheduler finds the `forever` loop in the run queue and passes control to it. The loop first calls `basic.showNumber(0)`. In the diagram below, we use “Show 0” to refer to the execution of this function: +The program then ends execution and control passes back to the scheduler. Let’s assume the user has not pressed any buttons . The scheduler finds the `forever` loop in the run queue and passes control to it. The loop first calls `basic.showNumber(0)`. In the diagram below, we use "Show 0" to refer to the execution of this function: ![Execution sequence diagram: display loop with increment and interrupt](/static/mb/device/reactive-3.png) -While "Show 0" (the blue sequence) is running, periodic interrupts by the scheduler (every 6 milliseconds) poll for button presses and queue an event handler for each press of button **A**. Let’s say that one button press takes place during this time, as shown above. This will cause an event handler (labelled “inc”) to be queued for later execution by the scheduler. Once the "Show 0" has completed, the loop then calls `basic.pause(20)` to put the forever loop to sleep for 20 milliseconds and give the scheduler an opportunity to run any newly queued event handler. Control passes to the “inc” event handler which will increment the global variable `count` from 0 to 1 and then complete, returning control to the scheduler. At some point, the `forever` loop moves from the sleep queue to the run queue; the `forever` loop then will resume and call `basic.showNumber(1)`. +While "Show 0" (the blue sequence) is running, periodic interrupts by the scheduler (every 6 milliseconds) poll for button presses and queue an event handler for each press of button **A**. Let’s say that one button press takes place during this time, as shown above. This will cause an event handler (labelled "inc") to be queued for later execution by the scheduler. Once the "Show 0" has completed, the loop then calls `basic.pause(20)` to put the forever loop to sleep for 20 milliseconds and give the scheduler an opportunity to run any newly queued event handler. Control passes to the "inc" event handler which will increment the global variable `count` from 0 to 1 and then complete, returning control to the scheduler. At some point, the `forever` loop moves from the sleep queue to the run queue; the `forever` loop then will resume and call `basic.showNumber(1)`. -## Final thoughts +## Final comments -Through this example, we have seen that the micro:bit scheduler enables you to create a program that is composed of concurrent subprograms. In essence, the programmer needs to only think about the concurrent subprograms cooperatively passing control back to the scheduler, making sure no subprogram hogs control (or “dribbles the ball without passing”) for too long. While a subprogram runs, the scheduler polls the buttons and other IO peripherals at a high frequency in order to fire off events and queue event handlers for later execution, but this is invisible to the programmer. +Through this example, we have seen that the micro:bit scheduler enables you to create a program that is composed of concurrent subprograms. In essence, the programmer needs to only think about the concurrent subprograms cooperatively passing control back to the scheduler, making sure no subprogram hogs control (or "dribbles the ball without passing") for too long. While a subprogram runs, the scheduler polls the buttons and other IO peripherals at a high frequency in order to fire off events and queue event handlers for later execution, but this is invisible to the programmer. As a result, you can easily add a new capability to the micro:bit by just adding a new subprogram. For example, if you want to add a reset feature to the counter program, all you need to do is add a new event handler for a press of button **B** that sets the global variable "count" to zero, as shown below: -```typescript +```typescript-ignore let count = 0 input.onButtonPressed(Button.A, () => { @@ -169,4 +179,3 @@ input.onButtonPressed(Button.B, () => { count = 0 }) ``` - diff --git a/docs/device/serial.md b/docs/device/serial.md index 8f9499acaf8..4dd4f9d677d 100644 --- a/docs/device/serial.md +++ b/docs/device/serial.md @@ -26,16 +26,19 @@ Unfortunately, using the serial library requires quite a bit of a setup. ### ~ hint -**Windows earlier than 10** +#### Windows earlier than 10 If you are running a Windows version earlier than 10, you must [install a device driver](https://os.mbed.com/docs/latest/tutorials/windows-serial-driver.html) (for the computer to recognize the serial interface of the micro:bit). -## ~ +### ~ + Also, if you don't see the serial port as one of your computer's devices, you might need to [update the firmware](/device/firmware) on the @boardname@. Find the device name for the attached serial port in the following instructions for your operating system. -### Windows > Tera Term +## Windows + +### Tera Term -* Install the terminal emulator [Tera Term](https://ttssh2.osdn.jp/index.html.en). At the time of this writing, the latest version is 4.88 and can be downloaded [from here](http://en.osdn.jp/frs/redir.php?m=jaist&f=%2Fttssh2%2F63767%2Fteraterm-4.88.exe). Follow the instructions from the installer. +* Install the terminal emulator [Tera Term](https://teratermproject.github.io/index-en.html). The the latest release and can be downloaded [from here](https://github.com/TeraTermProject/teraterm/releases). Scroll down to the "Assets" section and download the release package that is appropriate for your computer. For example, if you have an x64 based processor, click on the one that ends in "x64.exe". Once both the driver and the terminal emulator are installed, plug in the micro:bit and wait until the device is fully setup. Then, open TeraTerm. @@ -47,9 +50,9 @@ You should be good. Feel free to hit `Setup` > `Save Setup` in the menus to eras Please note that Windows will assign you a different COM port if you plug in another micro:bit. If you're juggling between micro:bits, you'll have to change the COM port every time. -### Windows > Putty +### Putty -If you prefer another terminal emulator (such as [PuTTY](http://www.putty.org/)), here are some instructions. +If you prefer another terminal emulator (such as [PuTTY](https://www.chiark.greenend.org.uk/~sgtatham/putty/)), here are some instructions. * Open Windows's [Device Manager](https://windows.microsoft.com/en-us/windows/open-device-manager); expand the section called "Ports (COM & LPT)"; write down the com number for "mbed Serial Port" (e.g. COM14) * Open PuTTY; on the main screen, use the following settings: Serial / COM14 / 115200. Replace COM14 with the COM port number you wrote down previously. Feel free to type in a name and hit "Save" to remember this configuration. diff --git a/docs/device/usb.md b/docs/device/usb.md index 8376909dd51..e55da14429a 100644 --- a/docs/device/usb.md +++ b/docs/device/usb.md @@ -1,10 +1,16 @@ -# Uploading programs to your @boardname@ +# Transferring programs to your @boardname@ -Most of the time you'll be writing and testing your programs in the [simulator](/device/simulator). Once you've finished your program though, you can **compile** it and run it on your @boardname@. Transferring your program to the @boardname@ is as simple as saving a file to a drive on your computer. +Most of the time you'll be writing and testing your programs in the [simulator](/device/simulator). Once you've finished your program though, you can **compile** it and run it on your @boardname@. Transferring your program to the @boardname@ is as simple as clicking the **Download** button, or by saving a file to a drive on your computer. -When you plug your @boardname@ into USB, a new drive is created with the **@drivename@** label. This is where you'll save your program. +![micro:bit connected using USB](/static/mb/device/usb-thin.jpg) -![](/static/mb/device/usb-thin.jpg) +## Transfer using a WebUSB connection + +With some newer browsers, you can transfer your program to the @boardname@ with a single click. If your browser supports WebUSB, you can use the **one-click download** feature to send your programs to the @boardname@. See the [WebUSB](/device/usb/webusb) page to learn how to pair your @boardname@ with a computer and transfer your programs with a single click. + +## Downloading your program as file + +If your browser doesn't support WebUSB or you want to use your computer's file system to transfer your program instead, you can download it to the @boardname@ as a file. When you plug your @boardname@ into USB, a new drive is created with the **@drivename@** label. This is where you'll save your program. The basic steps are: @@ -29,8 +35,10 @@ Here are instructions for different browsers on Windows and Mac computers. Choos * [Chrome](/device/usb/mac-chrome) * [Firefox](/device/usb/mac-firefox) -## ~hint +### ~hint + +#### Transfer problems? Transfer not working? See some [troubleshooting tips](/device/usb/troubleshoot). -## ~ +### ~ diff --git a/docs/device/usb/mac-chrome.md b/docs/device/usb/mac-chrome.md index 855582b9f5f..b785f999f6e 100644 --- a/docs/device/usb/mac-chrome.md +++ b/docs/device/usb/mac-chrome.md @@ -16,7 +16,7 @@ You need the following things to transfer and run a script on your micro:bit: * A-Male to Micro USB cable to connect your computer to your micro:bit. This is the same cable that is commonly used to connect a smart phone to a computer. -* A PC running Windows 7 or later, or a Mac running OS X 10.6 or later +* A Mac running OS X 10.9 or later. ## Step 1: Connect your micro:bit to your computer @@ -61,9 +61,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. +### ~hint -## ~hint +#### Transfer problems? Transfer not working? See some [troubleshooting tips](/device/usb/troubleshoot). -## ~ +### ~ diff --git a/docs/device/usb/mac-firefox.md b/docs/device/usb/mac-firefox.md index 0aef7055cce..d690a167b77 100644 --- a/docs/device/usb/mac-firefox.md +++ b/docs/device/usb/mac-firefox.md @@ -16,7 +16,7 @@ You need the following things to transfer and run a script on your micro:bit: * A-Male to Micro USB cable to connect your computer to your micro:bit. This is the same cable that is commonly used to connect a smart phone to a computer. -* A PC running Windows 7 or later, or a Mac running OS X 10.6 or later +* A Mac running OS X 10.9 or later. ## Step 1: Connect your micro:bit to your computer @@ -64,8 +64,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. -## ~hint +### ~hint + +#### Transfer problems? Transfer not working? See some [troubleshooting tips](/device/usb/troubleshoot). -## ~ +### ~ diff --git a/docs/device/usb/mac-safari.md b/docs/device/usb/mac-safari.md index e23fb43367b..402824c24bc 100644 --- a/docs/device/usb/mac-safari.md +++ b/docs/device/usb/mac-safari.md @@ -16,7 +16,7 @@ You need the following things to transfer and run a script on your micro:bit: * A-Male to Micro USB cable to connect your computer to your micro:bit. This is the same cable that is commonly used to connect a smart phone to a computer. -* A PC running Windows 7 or later, or a Mac running OS X 10.6 or later +* A Mac running OS X 10.9 or later. ## Step 1: Connect your micro:bit to your computer @@ -61,8 +61,10 @@ 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. -## ~hint +### ~hint + +#### Transfer problems? Transfer not working? See some [troubleshooting tips](/device/usb/troubleshoot). -## ~ +### ~ diff --git a/docs/device/usb/webusb.md b/docs/device/usb/webusb.md index 9f515411aed..566235b6db8 100644 --- a/docs/device/usb/webusb.md +++ b/docs/device/usb/webusb.md @@ -1,44 +1,94 @@ # WebUSB -[WebUSB](https://wicg.github.io/webusb/) is an emerging web standard that allows to access @boardname@ from web pages. -It allows for a **one-click download** without installing any additional app or software! It also allows to receive data from the @boardname@. +[WebUSB](https://wicg.github.io/webusb/) is a recent and developing web feature that allows you to access a @boardname@ directly from a web page. With MakeCode it allows for **one-click** downloads to your @boardname@ without installing an additional app or other software! It also lets you directly receive data into the MakeCode editor from the @boardname@. -## Support +https://youtu.be/PxfPs1zwKl0 -* Chrome 79+ browser for Android, Chrome OS, Linux, macOS and Windows 10. -* Microsoft Edge 79+ browser for Android, Chrome OS, Linux, macOS and Windows 10. +### ~ reminder -## Prepare your @boardname@ +#### WebUSB support for your @boardname@ -Make sure that your @boardname@ is running version **0249** or above of the firmware. Upgrading is as easy as dragging a file and it takes a few seconds to get it done. +If you're not using a current version of the Chrome or Microsoft Edge browsers, make sure they are this version or newer: -* [Check out the instructions to check and upgrade your @boardname@.](/device/usb/webusb/troubleshoot) +* Chrome (version 79 and newer) browser for Android, Chrome OS, Linux, macOS and Windows 10. +* Microsoft Edge (version 79 and newer) browser for Android, Chrome OS, Linux, macOS and Windows 10. + +Also, if you have a [@boardname@ V1 board](https://support.microbit.org/support/solutions/articles/19000119162-how-to-identify-the-version-number-of-your-micro-bit), make sure that it is running version **0249** or above of the firmware. Upgrading is as easy as dragging a file to a folder and it takes a few seconds to get it done. + +* Check out the [instructions](/device/usb/webusb/troubleshoot) to check and upgrade your @boardname@. + +### ~ ## Pair your @boardname@ -Here are the steps on the supported browsers: +The first time you pair your @boardname@ with your computer you'll need to go through a few easy steps to get setup. Here's how to get paired with WebUSB: -* connect your @boardname@ to your computer with the microUSB cable -* open a project -* click the triple dot icon on the **Download** button and click **Pair device** -* click on the **Pair device** button and select **BBC micro:bit CMSIS-DAP** or **DAPLink CMSIS-DAP** from the list. +### Download your project -If you don't see any devices in the list and @boardname@ has the right firmware (**0249** or above), you can create a [support ticket](https://support.microbit.org/support/tickets/new) to notify the Micro:bit Foundation of the problem. Skip the rest of these steps. +Once you've created or opened a project, and you're ready to download it to the @boardname@, click the **Download** button at the bottom of the editor window. -## Unpair your @boardname@ #unpair +![Download button and menu](/static/mb/device/usb/download-button-menu.png) + +### Connect the USB cable + +If you haven't connected it already, connect your @boardname@ to your computer with a [micro-USB](https://support.microbit.org/support/solutions/articles/19000037633-what-type-of-usb-lead-do-i-need-for-the-micro-bit-) cable. Then, click **Next** in the message window. + +![Connect device dialog](/static/mb/device/usb/connect-usb.png) + +### Pair the @boardname@ with your computer + +Another message window will display telling you to pair with the @boardname@ device. Click **Pair** to see to the device list. + +![Device name dialog](/static/mb/device/usb/pair-device.png) + +The @boardname@ will appear as either **BBC micro:bit CMSIS-DAP** or **DAPLink CMSIS-DAP** in the list. Select the device and click **Connect**. + +![Device list for WebUSB pairing](/static/mb/device/usb/select-device-pair.png) + +### ~ alert + +#### Don't see your micro:bit device? + +If you don't see any devices in the list and the @boardname@ is either a **V2** board or has the correct firmware version (**0249** or above), you can create a [support ticket](https://support.microbit.org/support/tickets/new) to notify the Micro:bit Foundation of the problem. You can skip the remaining steps. -You will need to unpair your device from the editor to disable WebUSB. +![Device list for WebUSB pairing](/static/mb/device/usb/no-pair-device.png) -* Click on the **lock** icon in the address bar -* Uncheck each **BBC micro:bit CMSIS-DAP** or **DAPLink CMSIS-DAP** device -* Reload the page +### ~ -![](/static/webusb/unpair.gif) +### You're connected! -## One-click Download +When your @boardname@ is connected, you'll see the **Connected to micro:bit** message window. Click on **Download** and you're project will transfer directly to the @boardname@! -Once your @boardname@ is paired, MakeCode will use WebUSB to transfer the code without having to drag and drop. Happy coding! +![Connected message window](/static/mb/device/usb/usb-connected.png) + +### ~ alert + +#### Connection failed? + +If the connection to your @boardname@ was unsuccessful, you'll see the **Failed to connect** message. You can press **Try Again** to attempt the connection again, download the project as a file instead, or cancel the window and [troubleshoot](/device/usb/webusb/troubleshoot) your connection. + +![Connect failed message window](/static/mb/device/usb/usb-connect-fail.png) + +### ~ + +## One-click downloads + +Once your @boardname@ is paired, MakeCode will use WebUSB to transfer the code directly and you won't have to drag and drop .hex files from a folder. Just click the **Download** button in the editor and your project code will just transfer to the @boardname@. ## Console output -MakeCode will be able to "listen" to your @boardname@ and display the console output. +Another feature of having a WebUSB connection is that MakeCode will be able to detect console output from your @boardname@ and display the console output in the editor. + +## Unpair your @boardname@ #unpair + +If you don't want to use WebUSB any longer, you will need to unpair your device from the editor to disable the WebUSB connection. + +1. Click on the **lock** icon in the address bar of the browser. +2. Uncheck each **BBC micro:bit CMSIS-DAP** or **DAPLink CMSIS-DAP** device displayed in the device list. +3. Reload the MakeCode editor page. + +![Unpairing from the browser](/static/download/browser-unpair-image.gif) + +## Hex file download tool + +Use The [Hex Download Tool](https://microbit.org/tools/webusb-hex-download-tool) from the Micro:bit Foundation to download hex files to one or more micro:bits directly with WebUSB (without going through MakeCode). diff --git a/docs/device/usb/webusb/troubleshoot.md b/docs/device/usb/webusb/troubleshoot.md index 029078c02b2..496a7d7b9e0 100644 --- a/docs/device/usb/webusb/troubleshoot.md +++ b/docs/device/usb/webusb/troubleshoot.md @@ -8,31 +8,43 @@ Having issues pairing your @boardname@ with [WebUSB](/device/usb/webusb)? Let's ## Step 1: Check your cable -Make sure that your @boardname@ is connected to your computer with a micro USB cable. You should see a **MICROBIT** drive appear in Windows Explorer when it's connected. +Make sure that your @boardname@ is connected to your computer with a micro USB cable. For example, in Windows Explorer you should see a **MICROBIT** drive appear when it's connected. ![MICROBIT drive](/static/mb/device/windows-microbit-drive.png) **If you can see the MICROBIT drive go to step 2**. If you can't see the drive: + * Make sure that the USB cable is working. >Does the cable work on another computer? If not, find a different cable to use. Some cables may only provide a power connection and don't actually transfer data. * Try another USB port on your computer. -Is the cable good but you still can't see the **MICROBIT** drive? Hmm, you might have a problem with your @boardname@. Try the additional steps described in the [fault finding](https://support.microbit.org/support/solutions/articles/19000024000-fault-finding-with-a-micro-bit) page at microbit.org. If this doesn't help, you can create a [support ticket](https://support.microbit.org/support/tickets/new) to notify the Micro:bit Foundation of the problem. **Skip the rest of these steps**. +Is the cable good but you still can't see the **MICROBIT** drive? Hmm, you might have a problem with your @boardname@. Try the additional steps described in the [fault finding](https://support.microbit.org/support/solutions/articles/19000024000-fault-finding-with-a-micro-bit) page at microbit.org. If this doesn't help, you can create a [support ticket](https://support.microbit.org/support/tickets/new) to notify the Micro:bit Foundation of the problem. **Skip the remaining troubleshooting steps**. ## Step 2: Check your firmware version -It's possible that the firmware version on the @boardname@ needs an update. Let's check: +If your downloads still aren't working, it's possible that the firmware version on the @boardname@ needs an update. Let's check: -1. Go to the **MICROBIT** drive. -2. Open the **DETAILS.TXT** file.
-![](/static/mb/device/mb-drive-contents.jpg)
-3. Look for a line in the file that says the version number. It should say **Version: \.\.\.** -![](/static/mb/device/details-txt.jpg) - or **Interface Version: \.\.\.** - ![](/static/mb/device/details-243.png) -
+### 1. Go to the MICROBIT drive + +Navigate to the **MICROBIT** drive in the computer's File Explorer. + +### 2. Open the DETAILS.TXT file + +Look for the **DETAILS.TXT** file and open it. + +![](/static/mb/device/mb-drive-contents.jpg) + +### 3. Find the firmware version number + +Look for a line in the file that says the version number. It should say **Version**: + +![Firmware version number in DETAILS.TXT](/static/mb/device/details-txt.jpg) + +or **Interface Version**: + +![Interface version number in DETAILS.TXT](/static/mb/device/details-243.png) If the version is **0234**, **0241**, **0243** you **NEED** to update the [firmware](/device/firmware) on your @boardname@. Go to **Step 3** and follow the upgrade instructions. @@ -40,25 +52,50 @@ If the version is **0249**, **0250** or higher, **you have the right firmware** ## Step 3: Upgrade the firmware -1. Put your @boardname@ into **MAINTENANCE Mode**. To do this, unplug the USB cable from the @boardname@ and then re-connect the USB cable while you hold down the reset button. Once you insert the cable, you can release the reset button. You should now see a **MAINTENANCE** drive instead of the **MICROBIT** drive like before. Also, a yellow LED light will stay on next to the reset button. +### 1. Put your @boardname@ into **MAINTENANCE Mode** + +To do this, unplug the USB cable from the @boardname@ and then reconnect the USB cable while you hold down the reset button. Once you insert the cable, you can release the reset button. You should now see a **MAINTENANCE** drive instead of the **MICROBIT** drive like before. Also, a yellow LED light will stay on next to the reset button. + ![MAINTENANCE gesture](/static/mb/device/maintenance.gif) -2. **[Download the firmware .hex file](https://microbit.org/guide/firmware/)** -3. Drag and drop that file onto the **MAINTENANCE** drive. -4. The yellow LED will flash while the `HEX` file is copying. When the copy finishes, the LED will go off and the @boardname@ resets. The **MAINTENANCE** drive now changes back to **MICROBIT**. -5. The upgrade is complete! You can open the **DETAILS.TXT** file to check and see that the firmware version changed to the match the version of the `HEX` file you copied. + +### 2. Download the firmware file + +Download the **[firmware .hex](https://microbit.org/guide/firmware/)** file. + +### 3. Transfer to the MAINTENANCE drive + +Drag and drop that file onto the **MAINTENANCE** drive. + +### 4. Look for the flashing LED + +The yellow LED will flash while the `HEX` file is copying. When the copy finishes, the LED will go off and the @boardname@ resets. The **MAINTENANCE** drive now changes back to **MICROBIT**. + +### 5. Upgrade complete + +The upgrade is complete! You can open the **DETAILS.TXT** file to check and see that the firmware version changed to the match the version of the `HEX` file you copied. ### ~hint +#### Firmware guide + If you want to know more about connecting the board, MAINTENANCE Mode, and upgrading the firmware, read about it in the [Firmware guide](https://microbit.org/guide/firmware/). ### ~ ## Step 4: Check your browser version -WebUSB is a fairly new feature and may require you to update your browser. Check that your browser version matches one of these: +WebUSB is a fairly new feature and may require you to update your browser. Check that your browser version matches one of those in the table below. + +Browser versions for Android, Chrome OS, Linux, macOS, and Windows 10, 11: -* Chrome 65+ for Android, Chrome OS, Linux, macOS and Windows 10. +| Browser | Version | +| - | - | +| Chrome | 61+ | +| Edge | 79+ | +| Safari | Not supported | +
+For other browsers, see the supported versions in the **[Can I use?](https://caniuse.com/?search=webusb)** table for WebUSB. ## Step 5: Pair device diff --git a/docs/device/usb/windows-chrome.md b/docs/device/usb/windows-chrome.md index 156830f48e6..5ef9c737cec 100644 --- a/docs/device/usb/windows-chrome.md +++ b/docs/device/usb/windows-chrome.md @@ -1,32 +1,22 @@ -# Uploading from Chrome for Windows - -## ~ hint - -Starting with Chrome 65 on Windows 10, -you can use **WebUSB** to download with one-click. -[Learn more about WebUSB...](/device/usb/webusb). - -## ~ +# Transferring from Chrome for Windows While you're writing and testing your programs, you'll mostly be [running them in the simulator](/device/simulator), but once you've finished your program you can **compile** it and run it on your micro:bit. -The basic steps are: +## Transfer using a WebUSB connection -1. Connect your micro:bit to your computer via USB -2. Click **Download** and download the `.hex` file -3. Copy the `.hex` file from your computer onto the micro:bit drive +With Chrome (version 79 and newer), you can transfer your program to the @boardname@ with a single click. If your browser supports WebUSB, you can use the **one-click download** feature to send your programs to the @boardname@. See the [WebUSB](/device/usb/webusb) page to learn how to pair your @boardname@ with a computer and transfer your programs with a single click. -## Requirements +## Downloading your program as file -You need the following things to transfer and run a script on your micro:bit: +The basic steps are: -* A-Male to Micro USB cable to connect your computer to your micro:bit. This is - the same cable that is commonly used to connect a smart phone to a computer. -* A PC running Windows 7 or later, or a Mac running OS X 10.6 or later +1. Connect your @boardname@ to your computer with a USB cable (use an A-Male to Micro USB cable) +2. Click **Download** and download the `.hex` file +3. Copy the `.hex` file from your computer onto the micro:bit drive -## Step 1: Connect your micro:bit to your computer +### Step 1: Connect your micro:bit to your computer First, connect the micro:bit: @@ -40,7 +30,7 @@ it appears as a new drive under Devices. ![](/static/mb/device/usb-windows-device.jpg) -## Step 2 (optional): Configure Chrome to ask where to save the file +### Step 2 (optional): Configure Chrome to ask where to save the file You only need to do this once. @@ -49,7 +39,7 @@ You only need to do this once. 3. Find the **Downloads** settings. 4. Enable the setting **Ask where to save each file before downloading**. -## Step 3: Download your program +### Step 3: Download your program 1. Open your project on @homeurl@ 2. Click **Download** @@ -57,11 +47,11 @@ You only need to do this once. so save it into the `MICROBIT` drive. Otherwise, continue with one of the options in Step 4 below. -## Step 4: Transfer the file to your micro:bit +### Step 4: Transfer the file to your micro:bit If the file was saved onto your computer, you will need to transfer it to the micro:bit. -## Manual transfer +#### Manual transfer Your `.hex` file (created in Step 3 above) appears as a download at the bottom of the browser. Click on the arrow next to the name of the file and then click **Show in folder**. @@ -74,7 +64,7 @@ Alternatively, right-click on the hex file, choose **Send to**, and then **MICRO ![](/static/mb/device/usb-windows-sendto.jpg) -## Step 5: After transferring the file +### Step 5: After transferring the file * The LED on the back of your micro:bit flashes during the transfer (which should only take a few seconds). @@ -85,8 +75,10 @@ Alternatively, right-click on the hex file, choose **Send to**, and then **MICRO 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. -## ~hint +### ~hint + +#### Transfer problems? Transfer not working? See some [troubleshooting tips](/device/usb/troubleshoot). -## ~ +### ~ diff --git a/docs/device/usb/windows-edge.md b/docs/device/usb/windows-edge.md index cd838b8718e..40928e0be9f 100644 --- a/docs/device/usb/windows-edge.md +++ b/docs/device/usb/windows-edge.md @@ -1,26 +1,24 @@ -# Uploading from Microsoft Edge on Windows +# Transferring from Microsoft Edge on Windows -How to compile, transfer, and run a program on your micro:bit on **Microsoft Edge**. +How to compile, transfer, and run a program on your micro:bit with **Microsoft Edge**. While you're writing and testing your programs, you'll mostly be [running them in the simulator](/device/simulator), but once you've finished your program you can **compile** it and run it on your micro:bit. -The basic steps are: +## Transfer using a WebUSB connection -1. Connect your @boardname@ to your computer via USB -2. Click **Download** to download the `.hex` file -3. Click the **Save As** button in the bottom bar and save the `.hex` file into the MICROBIT drive +With Microsoft Edge (version 79 and newer), you can transfer your program to the @boardname@ with a single click. If your browser supports WebUSB, you can use the **one-click download** feature to send your programs to the @boardname@. See the [WebUSB](/device/usb/webusb) page to learn how to pair your @boardname@ with a computer and transfer your programs with a single click. -## Requirements +## Downloading your program as file -You need the following things to transfer and run a script on your micro:bit: +The basic steps are: -* A-Male to Micro USB cable to connect your computer to your micro:bit. This is - the same cable that is commonly used to connect a smart phone to a computer. -* A PC running Windows 7 or later, or a Mac running OS X 10.6 or later +1. Connect your @boardname@ to your computer with a USB cable (use an A-Male to Micro USB cable) +2. Click **Download** to download the `.hex` file +3. Click the **Save As** button in the bottom bar and save the `.hex` file into the MICROBIT drive -## Step 1: Connect your micro:bit to your computer +### Step 1: Connect your micro:bit to your computer First, connect the micro:bit: @@ -34,7 +32,7 @@ it appears as a new drive under Devices. ![](/static/mb/device/usb-windows-device.jpg) -## Step 2: Download your program +### Step 2: Download your program 1. Open your project on @homeurl@ 2. Click **Download** @@ -65,8 +63,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. -## ~hint +### ~hint + +#### Transfer problems? Transfer not working? See some [troubleshooting tips](/device/usb/troubleshoot). -## ~ +### ~ diff --git a/docs/device/usb/windows-firefox.md b/docs/device/usb/windows-firefox.md index 72ce4976cdf..74229f1546c 100644 --- a/docs/device/usb/windows-firefox.md +++ b/docs/device/usb/windows-firefox.md @@ -18,7 +18,7 @@ You need the following things to transfer and run a script on your micro:bit: * A-Male to Micro USB cable to connect your computer to your micro:bit. This is the same cable that is commonly used to connect a smart phone to a computer. -* A PC running Windows 7 or later, or a Mac running OS X 10.6 or later +* A PC running Windows 7 or later. ## Step 1: Connect your micro:bit to your computer @@ -64,8 +64,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. -## ~hint +### ~hint + +#### Transfer problems? Transfer not working? See some [troubleshooting tips](/device/usb/troubleshoot). -## ~ +### ~ diff --git a/docs/device/usb/windows-ie.md b/docs/device/usb/windows-ie.md index fcfa5428abf..f38fd503bec 100644 --- a/docs/device/usb/windows-ie.md +++ b/docs/device/usb/windows-ie.md @@ -17,7 +17,7 @@ You need the following things to transfer and run a script on your micro:bit: * A-Male to Micro USB cable to connect your computer to your micro:bit. This is the same cable that is commonly used to connect a smart phone to a computer. -* A PC running Windows 7 or later, or a Mac running OS X 10.6 or later +* A PC running Windows 7 or later. ## Step 1: Connect your micro:bit to your computer @@ -63,8 +63,10 @@ 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. -## ~hint +### ~hint + +#### Transfer problems? Transfer not working? See some [troubleshooting tips](/device/usb/troubleshoot). -## ~ +### ~ diff --git a/docs/device/v2.md b/docs/device/v2.md index 2e4ca4d4332..10d7a3e7a7e 100644 --- a/docs/device/v2.md +++ b/docs/device/v2.md @@ -1,25 +1,49 @@ -# micro:bit V2 +# micro:bit v2 -The [micro:bit V2](https://microbit.org/new-microbit/) introduces a microphone, speaker, and capacitive touch input on the board's logo. The new blocks designed for the micro:bit V2 will not work with the micro:bit v1. +The [micro:bit v2](https://microbit.org/new-microbit/) introduces a microphone, speaker, and capacitive touch input on the board's logo. The new blocks designed for the micro:bit v2 will not work with the micro:bit v1. Let's learn how this works in MakeCode... -### ~ hint +## v2 Blocks -#### !BETA ZONE! +![works with micro:bit v2 only image](/static/v2/v2-only.png) +
-We are still working on upgrading the editor to utilize all the new features. If you have a micro:bit V2 and wish to try things out, please use **https://makecode.microbit.org/beta** and -report any bugs back to us! +Here are the standard blocks that **require** micro:bit v2 hardware to run. -### ~ +```block +let pressed = input.logoIsPressed() +let level = input.soundLevel() +soundExpression.giggle.play() +soundExpression.giggle.playUntilDone() +music.setBuiltInSpeakerEnabled(false) +input.setSoundThreshold(SoundThreshold.Loud, 128) +pins.touchSetMode(TouchTarget.P0, TouchTargetMode.Capacitive) +input.onSound(DetectedSound.Loud, function () {}) +input.onLogoEvent(TouchButtonEvent.Pressed, function () {}) +``` -## v2 Blocks +If your program tries to run any of these blocks on a micro:bit **v1** board, you will see the **927** error code scroll across your screen. + +```sim +basic.forever(function() { + basic.showNumber(927) + basic.pause(2000) +}) +``` + +### ~ alert + +#### v2 features in extension blocks + +if you're using blocks from a loaded extension, such as [Datalogger](/reference/datalogger), that are using any newer **v2** features on a **v1** board, you will also see the **927** error when your program tries to run those blocks. + +### ~ -![works with micro:bit V2 only image](/static/v2/v2-only.png) -The following blocks require the micro:bit V2 hardware to run. If you try a program with those blocks on a micro:bit V1 board, you see the ``927`` error code scroll across your screen. +### New blocks reference -![A screenshot of the v2 blocks](/static/v2/blocks.png) +The reference information for the new blocks introduced for micro:bit v2: ```cards input.onSound(DetectedSound.Loud, function () {}) @@ -32,28 +56,28 @@ music.setBuiltInSpeakerEnabled(false) pins.touchSetMode(TouchTarget.P0, TouchTargetMode.Capacitive) ``` -## How to recognize the micro:bit V2? +## How to recognize the micro:bit v2? -The first thing to know is whether you have a micro:bit v1 or micro:bit V2 at hand. You can recognize the v2 boards visually with these differences... +The first thing to know is whether you have a micro:bit v1 or micro:bit v2 at hand. You can recognize the v2 boards visually with these differences... * notches in the bottom edge connector * gold plated logo on the front, instead of a colored one * tiny hole near the top right of the screen for the microphone LED -![micro:bit v1 and micro:bit V2 front side by side](/static/v2/front.jpg) +![micro:bit v1 and micro:bit v2 front side by side](/static/v2/front.jpg) * red power LED next to the USB connect -* large black microphone component centrally located in the back and rotated by 45 degrees +* large black speaker component centrally located in the back and rotated by 45 degrees * slanted radio antenna -![micro:bit v1 and micro:bit V2 back side by side](/static/v2/back.jpg) +![micro:bit v1 and micro:bit v2 back side by side](/static/v2/back.jpg) ## v2 simulator -If your program uses any of the micro:bit V2 specific blocks, it will automatically change to a micro:bit V2, with notches in the connector and a gold plated logo. You will also see a "v2" symbol on the lower right of the board. +If your program uses any of the micro:bit v2 specific blocks, the simulator will automatically change to a micro:bit v2 with notches in the connector and a gold plated logo. You will also see a "v2" symbol on the lower right of the board. -![micro:bit V2 simulator](/static/v2/simulator.png) +![micro:bit v2 simulator](/static/v2/simulator.png) ## I see 927 scrolling on my board? -If you try to use a program with micro:bit V2 blocks on a micro:bit v1 board, you will see the **927** [error code](/device/error-codes) scroll on the micro:bit screen. +If your program tries to use any of the micro:bit v2 blocks on a micro:bit v1 board, you will see the **927** [error code](/device/error-codes) scroll on the micro:bit screen. diff --git a/docs/docs.md b/docs/docs.md index 30af5d9315e..a07b853040a 100644 --- a/docs/docs.md +++ b/docs/docs.md @@ -21,7 +21,7 @@ ## More questions? -* [Frequently Asked Question](/faq) +* [Frequently Asked Questions](/faq) * [Help Translate](/translate) * [Embedding project](/share) diff --git a/docs/domains.html b/docs/domains.html new file mode 100644 index 00000000000..47ca16ea196 --- /dev/null +++ b/docs/domains.html @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/errorhelper/ai-faq.md b/docs/errorhelper/ai-faq.md new file mode 100644 index 00000000000..51ed3824001 --- /dev/null +++ b/docs/errorhelper/ai-faq.md @@ -0,0 +1,23 @@ +# Microsoft MakeCode Error Helper + +## Responsible AI FAQ + +### 1. What is the MakeCode Error Helper? + +The MakeCode Error Helper is an AI-powered tool to help students understand coding errors in their projects. When an exception occurs, students can click an "Explain with AI" button in the problems window to get an AI-generated walkthrough of the issue. The tool analyzes the student's code and the specific exception to provide detailed, educational feedback about what went wrong and how to fix it. + +### 2. What can the MakeCode Error Helper do? + +The MakeCode Error Helper sends the student's current code along with the exception details to a Microsoft Azure LLM service. It returns a detailed walkthrough of the error in student-friendly language. + +### 3. What is MakeCode Error Helper's intended use? + +The MakeCode Error Helper is intended to help students learn from their coding mistakes by providing educational walkthroughs of exceptions. It aims to transform frustrating error messages into learning opportunities by explaining what went wrong and how to fix it in an understandable way. + +### 4. How was the MakeCode Error Helper evaluated? What metrics are used to measure performance? + +The system was evaluated with hundreds of coding errors and exceptions from student projects to ensure the responses are accurate, educational, and grounded. We evaluated accuracy with red teaming and expert review of responses. + +### 5. What are the limitations of the MakeCode Error Helper? How can users minimize the impact of the Error Helper's limitations when using the system? + +The Error Helper is designed specifically for compile-time and runtime exceptions in MakeCode programming environments. It may not perform well for complex or niche errors, and cannot be invoked for issues that don't generate exceptions. Students should still be encouraged to think through problems independently and to verify responses from the AI, using the Error Helper as a learning aid rather than a replacement for developing debugging skills. diff --git a/docs/extensions.md b/docs/extensions.md index 99c01550e68..50e2f0fe977 100644 --- a/docs/extensions.md +++ b/docs/extensions.md @@ -1,759 +1,70 @@ -# @extends +# Extensions -## Extension Gallery #gallery +Extensions are functional code modules that are installed from outside the MakeCode editor and plug new blocks into the **Toolbox**. These blocks are created by other authors or organizations to do things from simplifying coding tasks to working with hardware devices. -Check out [the accessories pages on microbit.org](https://microbit.org/buy/accessories/) for more information on these accessories and where to buy them. +### ~ reminder -### Categories +#### Extensions were known as "Packages" -```codecard -[{ - "name": "Display", - "url": "/extensions#display", - "cardType": "link" -}, { - "name": "Electronics", - "url": "/extensions#electronics", - "cardType": "link" -}, { - "name": "Gaming", - "url": "/extensions#gaming", - "cardType": "link" -}, { - "name": "Individual sensors", - "url": "/extensions#individual-sensors", - "cardType": "link" -}, { - "name": "IoT", - "url": "/extensions#iot", - "cardType": "link" -}, { - "name": "Kits", - "url": "/extensions#kits", - "cardType": "link" -}, { - "name": "LEDs and lights", - "url": "/extensions#iot", - "cardType": "link" -}, { - "name": "Machine learning", - "url": "/extensions#kits", - "cardType": "link" -}, { - "name": "Robotics", - "url": "/extensions#robotics", - "cardType": "link" -}, { - "name": "Sensor boards", - "url": "/extensions#sensor-boards", - "cardType": "link" -}, { - "name": "Sound", - "url": "/extensions#sound", - "cardType": "link" -}, { - "name": "Wearables", - "url": "/extensions#wearables", - "cardType": "link" -}] -``` +**Extensions** were previously called **Packages** in MakeCode. -## Display +### ~ -```codecard -[{ - "name": "Kitronik :VIEW text32", - "url": "/pkg/KitronikLtd/pxt-kitronik-viewtext32", - "cardType": "package" -}, { - "name": "XinaBox OD01 Display", - "url":"/pkg/xinabox/pxt-OD01", - "cardType": "package" -}, { - "name": "Tinkertanker ssd1306 OLED", - "url":"/pkg/Tinkertanker/pxt-oled-ssd1306", - "cardType": "package" -}, { - "name": "Tinkertanker ssd1306 OLED with reset pin", - "url":"/pkg/Tinkertanker/pxt-oled-ssd1306", - "cardType": "package" -}, { - "name": "Muselab ssd1306 OLED", - "url":"/pkg/MUSELAB/pxt-muselab-oled-v2", - "cardType": "package" -}, { - "name": "I2C LCD 1602 Display", - "url": "/pkg/1010Technologies/pxt-makerbit-ir-lcd1602", - "cardType": "package" -}] -``` +## Adding an extension to a project -## Electronics +You can add an extension by going to **Toolbox** and clicking on the **Extensions** category. -```codecard -[{ - "name": "Kitronik Stop:Bit", - "url":"/pkg/KitronikLtd/pxt-kitronik-stopbit", - "cardType": "package" -}, { - "name": "Kitronik Access:Bit", - "url":"/pkg/KitronikLtd/pxt-kitronik-accessbit", - "cardType": "package" -}, { - "name": "PCA9685 LED controller", - "url":"/pkg/jdarling/pxt-pca9685", - "cardType": "package" -}, { - "name": "Coolguy expansion board", - "url":"/pkg/CoolGuy-official/pxt-coolguy", - "cardType": "package" -}] -``` +![Extensions Toolbox category](/static/extensions/toolbox-category.png) -## Gaming +This will open a window giving you a place to search for extensions. Also, a selection of recommended extensions is shown for you to choose from. -```codecard -[{ - "name": "Elecfreaks magic wand", - "url":"/pkg/elecfreaks/pxt-magicwand", - "cardType": "package" -}, { - "name": "Kitronik :GAME ZIP64", - "url":"/pkg/KitronikLtd/pxt-kitronik-zip-64", - "cardType": "package" -}, { - "name": "Kitronik :GAME Controller", - "url":"/pkg/KitronikLtd/pxt-kitronik-game-controller", - "cardType": "package" -}, { - "name": "Sparkfun Gamer:bit", - "url":"/pkg/sparkfun/pxt-gamer-bit", - "cardType": "package" -}, { - "name": "4tronix BitCommander", - "url":"/pkg/4tronix/BitCommander", - "cardType": "package" -}, { - "name": "51bit SFC/NES controller", - "url":"/pkg/51bit/SFC", - "cardType": "package" -}, { - "name": "Pimoroni touch:bit", - "url":"/pkg/pimoroni/pxt-touchbit", - "cardType": "package" -}, { - "name": "ALS Robot JoyBit", - "url":"/pkg/alsrobot-microbit-makecode-packages/ALSRobotJoyBit", - "cardType": "package" -}] -``` +![Extensions Window](/static/extensions/extensions-window.gif) -## Individual sensors +When you select an extension, you should see the new extension category appear in the Toolbox of your project. -```codecard -[{ - "name": "DS3231", - "url":"/pkg/keble6/pxt-DS3231", - "cardType": "package" -}, { - "name": "Let's Talk Science COZIR sensor", - "url":"/pkg/letstalkscience/pxt-cozir", - "cardType": "package" -}, { - "name": "MAX6675", - "url":"/pkg/microsoft/pxt-max6675", - "cardType": "package" -}, { - "name": "Sonar", - "url":"/pkg/microsoft/pxt-sonar", - "cardType": "package" -}, { - "name": "Non-blocking Ultrasonic Sensing", - "url":"/pkg/1010Technologies/pxt-makerbit-ultrasonic", - "cardType": "package" -}, { - "name": "HX711 Weight Sensor", - "url":"/pkg/daferdur/pxt-myHX711", - "cardType": "package" -}, { - "name": "Bluetooth Temperature Sensor", - "url":"/pkg/microsoft/pxt-bluetooth-temperature-sensor", - "cardType": "package" -}, { - "name": "Bluetooth MAX6675", - "url":"/pkg/microsoft/pxt-bluetooth-max6675", - "cardType": "package" -}, { - "name": "ky040 rotary", - "url":"/pkg/Tinkertanker/pxt-rotary-encoder-ky040", - "cardType": "package" -}, { - "name": "GY521", - "url":"/pkg/PaulDFoster/pxt-microbit-GY521", - "cardType": "package" -}, { - "name": "DHT11 & DHT22 Temperature and Humidity", - "url":"/pkg/alankrantas/pxt-DHT11_DHT22", - "cardType": "package" -}, { - "name": "gator:light Light sensor", - "url":"/pkg/sparkfun/pxt-gator-light", - "cardType": "package" -}, { - "name": "gator:temp Temperature Sensor", - "url":"/pkg/sparkfun/pxt-gator-temp", - "cardType": "package" -}, { - "name": "gator:microphone Microphone", - "url":"/pkg/sparkfun/pxt-gator-microphone", - "cardType": "package" -}, { - "name": "gator:soil Soil Sensor", - "url":"/pkg/sparkfun/pxt-gator-soil", - "cardType": "package" -}, { - "name": "gator:temp Particle Sensor", - "url":"/pkg/sparkfun/pxt-gator-particle", - "cardType": "package" -}, { - "name": "MonkMakes Sensor", - "url":"/pkg/monkmakes/pxt-sensor", - "cardType": "package" -}, { - "name": "ALS Robot Electromagnet", - "url":"/pkg/alsrobot-microbit-makecode-packages/ALSRobotElectromagnet", - "cardType": "package" -}, { - "name": "MakerBit Touch MPR121", - "url": "/pkg/1010Technologies/pxt-makerbit-touch", - "cardType": "package" -}, { - "name": "Keyestudio Infrared Receiver", - "url": "/pkg/1010Technologies/pxt-makerbit-ir-receiver", - "cardType": "package" -}, { - "name": "BMP280 Barrometer", - "url": "/pkg/rebeccaclavier/pxt-bmp280", - "cardType": "package" -}, { - "name": "STTS751 temperature Sensor", - "url": "/pkg/makecode-extensions/STTS751", - "cardType": "package" -}, { - "name": "LSM6DSO Accelerometer/Gyroscope", - "url": "/pkg/makecode-extensions/LSM6DSO", - "cardType": "package" -}, { - "name": "LPS22 Pressure", - "url": "/pkg/makecode-extensions/LPS22", - "cardType": "package" -}, { - "name": "LIS2DW12 motion sensor", - "url": "/pkg/makecode-extensions/LIS2DW12", - "cardType": "package" -}, { - "name": "LIS2MDL magnetic sensor", - "url": "/pkg/makecode-extensions/LIS2MDL", - "cardType": "package" -}, { - "name": "HTS221 Humidity and temperature", - "url": "/pkg/makecode-extensions/HTS221", - "cardType": "package" -}] -``` +![New added extension in Toolbox](/static/extensions/new-extension.png) -## IoT +The Toolbox category will contain the extension's blocks, ready for you to use in your project's code. -```codecard -[{ - "name": "Hardwario IoT Kit", - "url":"/pkg/hardwario/pxt-microbit-hardwario", - "cardType": "package" -}, { - "name": "Pi Supply Lora Node", - "url":"/pkg/PiSupply/pxt-iot-lora-node", - "cardType": "package" -}, { - "name": "WiFi:Bit", - "url":"/pkg/e-radionicacom/pxt-wifi", - "cardType": "package" -}, { - "name": "ESP8266 and ThingSpeak", - "url":"/pkg/alankrantas/pxt-ESP8266_ThingSpeak", - "cardType": "package" -}, { - "name": "DFRobot microIoT board", - "url":"/pkg/DFRobot/pxt-DFRobot-microIoT", - "cardType": "package" -}, { - "name": "Muselab WiFi IoT Shield", - "url":"/pkg/MUSELAB/pxt-wifi-shield", - "cardType": "package" -}] -``` +![Blocks in the added extension](/static/extensions/extension-blocks.png) -## Kits +### ~ hint -```codecard -[{ - "name": "Pi Supply tinker:kit", - "url": "/pkg/PiSupply/pxt-tinker-kit", - "cardType": "package" -}, { - "name": "Freenove Starter Kit", - "url": "/pkg/Freenove/Makecode-Extension-Starter-Kit", - "cardType": "package" -}, { - "name": "Elecfreaks PlanetX sensor kit", - "url":"/pkg/elecfreaks/pxt-PlanetX", - "cardType": "package" -}, { - "name": "Inksmith Climate Action Kit", - "url":"/pkg/dugbraden/pxt-climate-action-kit", - "cardType": "package" -}, { - "name": "Grove inventor kit", - "url":"/pkg/Seeed-Studio/pxt-grove", - "cardType": "package" -}, { - "name": "Minode Kit", - "url":"/pkg/minodekit/pxt-minode", - "cardType": "package" -}] -``` +#### Extension gallery -## LEDs and lights +For a list of extensions within categories, browse the [Extension Gallery](/extensions/extension-gallery). -```codecard -[{ - "name": "Kitronik Lamp:Bit", - "url":"/pkg/KitronikLtd/pxt-kitronik-lampbit", - "cardType": "package" -}, { - "name": "Kitronik Halo HD", - "url":"/pkg/KitronikLtd/pxt-kitronik-halohd", - "cardType": "package" -}, { - "name": "NeoPixel", - "url":"/pkg/microsoft/pxt-neopixel", - "cardType": "package" -}, { - "name": "WS2812B", - "url": "/pkg/microsoft/pxt-ws2812b", - "cardType": "package" -}, { - "name": "4tronix Cube:Bit", - "url":"/pkg/4tronix/cubebit", - "cardType": "package" -}, { - "name": "51bit ColorBit", - "url":"/pkg/51bit/ColorBit", - "cardType": "package" -}, { - "name": "Kitronik Zip Tile", - "url":"/pkg/KitronikLtd/pxt-kitronik-zip-tile", - "cardType": "package" -}, { - "name": "MAX7219 8x8", - "url":"/pkg/alankrantas/pxt-MAX7219_8x8", - "cardType": "package" -}] -``` +### ~ -## Machine learning +## Removing an extension from a project -```codecard -[{ - "name": "MU Vision camera", - "url":"/pkg/mu-opensource/pxt-muvision", - "cardType": "package" -}, { - "name": "DFRobot HuskyLens", - "url":"/pkg/DFRobot/pxt-DFRobot_HuskyLens", - "cardType": "package" -}] -``` -## Robotics +To remove an extension from a project, click on the Language toggle to move the project into **JavaScript** or **Python** view. Then expand the **Explorer** view under the micro:bit simulator. Click on the **Delete** button next to the extension you would like to remove. -```codecard -[{ - "name": "Finch 2.0", - "url":"/pkg/BirdBrainTechnologies/pxt-finch", - "cardType": "package" -}, { - "name": "Bouw je BEP", - "url":"/pkg/Bouw-je-BEP/Bouw-je-BEP", - "cardType": "package" -}, { - "name": "DF Robot Maqueen Plus", - "url":"/pkg/DFRobot/pxt-DFRobot-Maqueenplus", - "cardType": "package" -}, { - "name": "Joy IT Joy Car", - "url":"/pkg/joy-it/Joy-Car", - "cardType": "package" -}, { - "name": "Kitronik :MOVE Motor", - "url":"/pkg/KitronikLtd/pxt-kitronik-move-motor", - "cardType": "package" -}, { - "name": "A4 Technologies CODO", - "url":"/pkg/CODOmicrobit/pxt-CODO", - "cardType": "package" -}, { - "name": "Strawbees Robotic Inventions Kit", - "url":"/pkg/strawbees/pxt-robotic-inventions", - "cardType": "package" -}, { - "name": "Kitronik :MOVE mini", - "url":"/pkg/KitronikLtd/pxt-kitronik-servo-lite", - "cardType": "package" -}, { - "name": "Kitronik Integrated Robotics Board", - "url":"/pkg/KitronikLtd/pxt-kitronik-robotics-board", - "cardType": "package" -}, { - "name": "Kitronik Motor Driver Board", - "url":"/pkg/KitronikLtd/pxt-kitronik-motor-driver", - "cardType": "package" -}, { - "name": "Kitronik 16 Servo Board", - "url":"/pkg/KitronikLtd/pxt-kitronik-i2c-16-servo", - "cardType": "package" -}, { - "name": "YFROBOT Valon", - "url":"/pkg/YFROBOT-TM/pxt-yfrobot-valon", - "cardType": "package" -}, { - "name": "4tronix BitBot", - "url":"/pkg/4tronix/BitBot", - "cardType": "package" -},{ - "name": "4tronix Orbit", - "url":"/pkg/4tronix/Orbit", - "cardType": "package" -}, { - "name": "4tronix Drive:Bit", - "url":"/pkg/4tronix/DriveBit", - "cardType": "package" -}, { - "name": "4tronix Servo:Bit", - "url":"/pkg/4tronix/ServoBit", - "cardType": "package" -}, { - "name": "4tronix MiniBit", - "url":"/pkg/4tronix/MiniBit", - "cardType": "package" -}, { - "name": "Elecfreaks TPBot", - "url":"/pkg/elecfreaks/pxt-TPBot", - "cardType": "package" -}, { - "name": "DF Robot Maqueen", - "url":"/pkg/DFRobot/pxt-maqueen", - "cardType": "package" -}, { - "name": "Sunfounder Sloth", - "url":"/pkg/sunfounder/pxt-sloth", - "cardType": "package" -}, { - "name": "Sphero RVR", - "url":"/pkg/sphero-inc/sphero-sdk-microbit-makecode", - "cardType": "package" -}, { - "name": "Sparkfun Moto:bit", - "url":"/pkg/sparkfun/pxt-moto-bit", - "cardType": "package" -}, { - "name": "EBOTICS MIBO", - "url":"/pkg/EBOTICS/pxt-eboticsMIBO", - "cardType": "package" -}, { - "name": "ALSRobot MinCruise", - "url":"/pkg/alsrobot-microbit-makecode-packages/MiniCruise", - "cardType": "package" -}, { - "name": "ReroKit rero:micro", - "url":"/pkg/ReRoKit/pxt-reromicro", - "cardType": "package" -}, { - "name": "PLEN bit", - "url":"/pkg/plenprojectcompany/pxt-PLENbit", - "cardType": "package" -}, { - "name": "UCL Junk Robot", - "url":"/pkg/chevyng/pxt-ucl-junkrobot", - "cardType": "package" -}, { - "name": "Elecfreaks Cutebot", - "url":"/pkg/elecfreaks/pxt-cutebot", - "cardType": "package" -}, { - "name": "Kittenbot RobotBit", - "url":"/pkg/kittenbot/pxt-robotbit", - "cardType": "package" -}, { - "name": "inex iBit", - "url":"/pkg/emwta/pxt-iBit", - "cardType": "package" -}, { - "name": "InkSmith k8 robotics kit", - "url":"/pkg/k8robotics/pxt-k8", - "cardType": "package" -}, { - "name": "Freenove Micro:Rover", - "url":"/pkg/Freenove/Makecode-Extension-Rover", - "cardType": "package" -}, { - "name": "Gigglebot", - "url":"/pkg/dexterind/pxt-giggle", - "cardType": "package" -}, { - "name": "Robobit", - "url":"/pkg/4tronix/Robobit", - "cardType": "package" -}, { - "name": "Pi Supply Bit:Buggy", - "url":"/pkg/PiSupply/pxt-bitbuggy", - "cardType": "package" -}, { - "name": "ALS Robot Coo Coo", - "url":"/pkg/alsrobot-microbit-makecode-packages/CooCoo", - "cardType": "package" -}, { - "name": "ALS Robot CruiseBit", - "url":"/pkg/alsrobot-microbit-makecode-packages/CruiseBit", - "cardType": "package" -}, { - "name": "Hummingbird Bit", - "url":"/pkg/BirdBrainTechnologies/pxt-hummingbird-bit", - "cardType": "package" -}, { - "name": "Inex iKB-1 controller board", - "url":"/pkg/jcubuntu/pxt-iKB1", - "cardType": "package" -}, { - "name": "MakerBit motor controller", - "url":"/pkg/1010Technologies/pxt-makerbit-motor", - "cardType": "package" -}, { - "name": "Tobbie II", - "url":"/pkg/kaku111/pxt-tobbieII", - "cardType": "package" -}, { - "name": "Kitronik ACCESS:bit", - "url":"/pkg/KitronikLtd/pxt-kitronik-accessbit", - "cardType": "package" -}, { - "name": "Kitronik Fischertechnik interface", - "url":"/pkg/KitronikLtd/pxt-kitronik-fischertechnik", - "cardType": "package" -}, { - "name": "Keigan Motor", - "url": "/pkg/keigan-motor/pxt-KeiganMotor", - "cardType": "package" -}, { - "name": "TCEA Nexus:bit and Nexusbot", - "url":"/pkg/beyond-coding-tw/pxt-nexusbot", - "cardType": "package" -}, { - "name": "Kitronik Klip Motor", - "url":"/pkg/KitronikLtd/pxt-kitronik-klip-motor", - "cardType": "package" -}, { - "name": "Keyestudio Robot Car", - "url":"/pkg/Veilkrand/pxt-RobotCar", - "cardType": "package" -}, { - "name": "TinkerTanker Stepper Motor", - "url":"/pkg/Tinkertanker/pxt-stepper-motor", - "cardType": "package" -}] -``` +![File Explorer](/static/extensions/file-explorer.png) -## Sensor boards +## What extensions are loaded in my project? -```codecard -[{ - "name": "DFRobot Natural Science Board", - "url":"/pkg/DFRobot/pxt-DFRobot-NaturalScience", - "cardType": "package" -}, { - "name": "Kitronik Klimate Board", - "url":"/pkg/KitronikLtd/pxt-kitronik-klimate", - "cardType": "package" -}, { - "name": "Kitronik Smart Greenhouse", - "url":"/pkg/KitronikLtd/pxt-kitronik-smart-greenhouse", - "cardType": "package" -}, { - "name": "Make&Learn micro:shield", - "url":"/pkg/MakeAndLearn/pxt-microshield", - "cardType": "package" -}, { - "name": "Sparkfun Weather:bit", - "url":"/pkg/sparkfun/pxt-weather-bit", - "cardType": "package" -}, { - "name": "Sparkfun gator:environment", - "url":"/pkg/sparkfun/pxt-gator-environment", - "cardType": "package" -}, { - "name": "XinaBox SW01 Advanced Weather Sensor", - "url":"/pkg/xinabox/pxt-SW01", - "cardType": "package" -}, { - "name": "Cytron Edubit", - "url":"/pkg/CytronTechnologies/pxt-edubit", - "cardType": "package" -}, { - "name": "Imagimaker Magisheild", - "url":"/pkg/Imagimaker/pxt-imagimaker", - "cardType": "package" -}, { - "name": "Kitronik clip detector", - "url": "/pkg/KitronikLtd/pxt-kitronik-clip-detector", - "cardType": "package" -}, { - "name": "Pimoroni Envirobit", - "url": "/pkg/pimoroni/pxt-envirobit", - "cardType": "package" -}, { - "name": "Pimoroni Automationbit", - "url":"/pkg/pimoroni/pxt-automationbit", - "cardType": "package" -}, { - "name": "51bit Smart Tools", - "url": "/pkg/51bit/SmartTools", - "cardType": "package" -}, { - "name": "MakerBit", - "url": "/pkg/1010Technologies/pxt-makerbit", - "cardType": "package" -}, { - "name": "MakerBit Pins", - "url": "/pkg/1010Technologies/pxt-makerbit-pins", - "cardType": "package" -}, { - "name": "Elecfreaks Wukon", - "url": "/pkg/elecfreaks/pxt-wukong", - "cardType": "package" -}, { - "name": "Elite Longanbit", - "url": "/pkg/longan-link/pxt-longanbit", - "cardType": "package" -}, { - "name": "Adafruit Crickit", - "url": "/pkg/adafruit/pxt-crickit", - "cardType": "package" -}, { - "name": "Adafruit Seesaw", - "url": "/pkg/adafruit/pxt-seesaw", - "cardType": "package" -}] -``` +To determine which extensions your project is currently using, you can simply open the project in MakeCode and look at the Toolbox to see the custom categories that are displayed. If you need more information, such as the repository path or version of the extension, open the project in MakeCode and select **Project Settings** from the **Settings** menu in the top right corner of the screen. -## Sound +![Settings menu](/static/extensions/settings-menu.png) -```codecard -[{ - "name": "Kitronik Klef Piano", - "url":"/pkg/KitronikLtd/pxt-kitronik-klef-piano", - "cardType": "package" -}, { - "name": "Catalex Serial MP3 Player v1.0", - "url": "/pkg/1010Technologies/pxt-makerbit-mp3", - "cardType": "package" -}, { - "name": "51bit DFPlayer mini", - "url":"/pkg/51bit/dfplayermini", - "cardType": "package" -}] -``` +**Select Edit Settings As text** button. -## Wearables +![Edit settings button](/static/extensions/edit-settings-button.png) -```codecard -[{ - "name": "Bright Wearables Bright Board", - "url":"/pkg/BrightWearables/pxt-microbit-brightboard", - "cardType": "package" -}] -``` +The project settings will appear as text and you can see the extensions used in your project. They are listed under `"dependencies"`: -## Utilities -```codecard -[{ - "name": "DS3231 Real Time Clock", - "url":"/pkg/AlexandreFrolov/DS3231", - "cardType": "package" -}, { - "name": "Time & Date", - "url":"/pkg/bsiever/microbit-pxt-timeanddate", - "cardType": "package" -}, { - "name": "Kitronik Realtime Clock", - "url":"/pkg/KitronikLtd/pxt-kitronik-rtc", - "cardType": "package" -}, { - "name": "Code Dojo Olney", - "url":"/pkg/CoderDojoOlney/pxt-olney", - "cardType": "package" -}, { - "name": "Inventura textbook", - "url":"/pkg/assirati/pxt-inventura", - "cardType": "package" -}, { - "name": "File System", - "url":"/pkg/microsoft/pxt-filesystem", - "cardType": "package" -}, { - "name": "micro:turtle", - "url":"/pkg/microsoft/pxt-microturtle", - "cardType": "package" -}, { - "name": "MIDI", - "url":"/pkg/microsoft/pxt-midi", - "cardType": "package" -}, { - "name": "Bluetooth MIDI", - "url":"/pkg/microsoft/pxt-bluetooth-midi", - "cardType": "package" -}, { - "name": "BlockyTalkyBLE", - "url":"/pkg/LaboratoryForPlayfulComputation/pxt-BlockyTalkyBLE", - "cardType": "package" -}, { - "name": "Katakana", - "url":"/pkg/mbitfun/pxt-katakana", - "cardType": "package" -}, { - "name": "LINE BLE beacon", - "url":"/pkg/pizayanz/pxt-linebeacon", - "cardType": "package" -}, { - "name": "Pimoroni Scrollbit", - "url":"/pkg/pimoroni/pxt-scrollbit", - "cardType": "package" -}, { - "name": "SBRICK", - "url":"/pkg/vengit/pxt-sbrick", - "cardType": "package" -}, { - "name": "Annikken Andee", - "url":"/pkg/Annikken/pxt-Andee", - "cardType": "package" -}, { - "name": "Proportional Font", - "url":"/pkg/lwchkg/pxt-proportional-font", - "cardType": "package" -}, { - "name": "ALS Robot Keyboard", - "url":"/pkg/alsrobot-microbit-makecode-packages/ALSRobotKeyboard", - "cardType": "package" -}, { - "name": "Elecfreaks NeZha", - "url": "/pkg/elecfreaks/pxt-nezha", - "cardType": "package" -}] ``` +"dependencies": { + "core": "*", + "radio": "*", + "microphone": "*", + "maqueen": "github.com:dfrobot/pxt-maqueen#v1.7.2" +}, +``` + +The extensions with just a path of `"*"` are those included by default with the editor. Others are external and have a repository path, possibly with a version specified. + +## Custom extensions + +The [Build Your Own Extension](https://makecode.com/extensions/getting-started) manual is available for advanced users who want to publish their own extension. diff --git a/docs/extensions/build-your-own.md b/docs/extensions/build-your-own.md index 6f8b6711e57..9fc315c638d 100644 --- a/docs/extensions/build-your-own.md +++ b/docs/extensions/build-your-own.md @@ -17,3 +17,8 @@ Go to the MakeCode extension documentation and see the [getting started](https:/ Extensions are also known, and referred to, as _packages_. The term _package_ is currently used in the MakeCode documentation for developing and deploying extensions. ## ~ + + +## See also + +[Simulator extensions](./simulator-extensions.md) diff --git a/docs/extensions/extension-gallery.md b/docs/extensions/extension-gallery.md new file mode 100644 index 00000000000..bdf3958585b --- /dev/null +++ b/docs/extensions/extension-gallery.md @@ -0,0 +1,1283 @@ +# Extension Gallery + +### ~ hint + +#### Accessories + +Many extensions are available to work with interface kits, add-on hardware, or other devices and accessories. Check out [the accessories pages on microbit.org](https://microbit.org/buy/accessories/) for more information on these accessories and where to buy them. + +### ~ + +## Categories + +```codecard +[{ + "name": "Display", + "url": "/extensions/extension-gallery#display", + "cardType": "link" +}, { + "name": "Electronics", + "url": "/extensions/extension-gallery#electronics", + "cardType": "link" +}, { + "name": "Gaming", + "url": "/extensions/extension-gallery#gaming", + "cardType": "link" +}, { + "name": "Individual sensors", + "url": "/extensions/extension-gallery#individual-sensors", + "cardType": "link" +}, { + "name": "IoT", + "url": "/extensions/extension-gallery#iot", + "cardType": "link" +}, { + "name": "Kits", + "url": "/extensions/extension-gallery#kits", + "cardType": "link" +}, { + "name": "LEDs and lights", + "url": "/extensions/extension-gallery#leds-and-lights", + "cardType": "link" +}, { + "name": "Machine learning", + "url": "/extensions/extension-gallery#machine-learning", + "cardType": "link" +}, { + "name": "Robotics", + "url": "/extensions/extension-gallery#robotics", + "cardType": "link" +}, { + "name": "Sensor boards", + "url": "/extensions/extension-gallery#sensor-boards", + "cardType": "link" +}, { + "name": "Sound", + "url": "/extensions/extension-gallery#sound", + "cardType": "link" +}, { + "name": "Wearables", + "url": "/extensions/extension-gallery#wearables", + "cardType": "link" + }, { + "name": "Utilities", + "url": "/extensions/extension-gallery#utilities", + "cardType": "link" +}] +``` + +## Display + +```codecard +[{ + "name": "TM1638", + "url": "/pkg/NathanPervin/pxt-tm1638", + "cardType": "package" +}, { + "name": "Pythom1234 OLED Display SSD1306 128x64", + "url": "/pkg/Pythom1234/pxt-oled", + "cardType": "package" +}, { + "name": "Joy-IT RB-TFT1.8", + "url": "/pkg/joy-it/pxt-RB-TFT1.8", + "cardType": "package" +}, { + "name": "Kitronik 128x64 Display", + "url": "/pkg/KitronikLtd/pxt-kitronik-128x64Display", + "cardType": "package" +}, { + "name": "Monk Makes 7-Segment", + "url": "/pkg/monkmakes/monkmakes-7-segment", + "cardType": "package" +}, { + "name": "Pimoroni inky:bit", + "url": "/pkg/pimoroni/pxt-inkybit", + "cardType": "package" +}, { + "name": "Kitronik :VIEW text32", + "url": "/pkg/KitronikLtd/pxt-kitronik-viewtext32", + "cardType": "package" +}, { + "name": "XinaBox OD01 Display", + "url":"/pkg/xinabox/pxt-OD01", + "cardType": "package" +}, { + "name": "Tinkertanker ssd1306 OLED", + "url":"/pkg/Tinkertanker/pxt-oled-ssd1306", + "cardType": "package" +}, { + "name": "Tinkertanker ssd1306 OLED with reset pin", + "url":"/pkg/Tinkertanker/pxt-oled-ssd1306", + "cardType": "package" +}, { + "name": "Muselab ssd1306 OLED", + "url":"/pkg/MUSELAB/pxt-muselab-oled-v2", + "cardType": "package" +}, { + "name": "I2C LCD 1602 Display", + "url": "/pkg/1010Technologies/pxt-makerbit-ir-lcd1602", + "cardType": "package" +}] +``` + +## Electronics + +```codecard +[{ + "name": "Kitronik Stop:Bit", + "url":"/pkg/KitronikLtd/pxt-kitronik-stopbit", + "cardType": "package" +}, { + "name": "Kitronik Access:Bit", + "url":"/pkg/KitronikLtd/pxt-kitronik-accessbit", + "cardType": "package" +}, { + "name": "PCA9685 LED controller", + "url":"/pkg/jdarling/pxt-pca9685", + "cardType": "package" +}, { + "name": "Coolguy expansion board", + "url":"/pkg/CoolGuy-official/pxt-coolguy", + "cardType": "package" +}] +``` + +## Gaming + +```codecard +[{ + "name": "Coderdojo Controller", + "url":"/pkg/jimd80/pxt-coderdojo-controller", + "cardType": "package" +}, { + "name": "Kittenbot JoyFrog", + "url":"/pkg/KittenBot/pxt-joyfrog", + "cardType": "package" +}, { + "name": "Elecfreaks magic wand", + "url":"/pkg/elecfreaks/pxt-magicwand", + "cardType": "package" +}, { + "name": "Kitronik :GAME ZIP64", + "url":"/pkg/KitronikLtd/pxt-kitronik-zip-64", + "cardType": "package" +}, { + "name": "Kitronik :GAME Controller", + "url":"/pkg/KitronikLtd/pxt-kitronik-game-controller", + "cardType": "package" +}, { + "name": "Sparkfun Gamer:bit", + "url":"/pkg/sparkfun/pxt-gamer-bit", + "cardType": "package" +}, { + "name": "4tronix BitCommander", + "url":"/pkg/4tronix/BitCommander", + "cardType": "package" +}, { + "name": "51bit SFC/NES controller", + "url":"/pkg/51bit/SFC", + "cardType": "package" +}, { + "name": "Pimoroni touch:bit", + "url":"/pkg/pimoroni/pxt-touchbit", + "cardType": "package" +}, { + "name": "ALS Robot JoyBit", + "url":"/pkg/alsrobot-microbit-makecode-packages/ALSRobotJoyBit", + "cardType": "package" +}] +``` + +## Individual sensors + +```codecard +[{ + "name": "SEN66 Air Quality Sensor", + "url":"/pkg/bsiever/pxt-sen66", + "cardType": "package" +}, { + "name": "KY-040 Rotary Encoder Plus", + "url":"/pkg/steveturbek/pxt-rotary-encoder-KY-040-plus", + "cardType": "package" +}, { + "name": "MonkMakes Plant Monitor", + "url":"/pkg/monkmakes/plant-monitor-makecode", + "cardType": "package" +}, { + "name": "SGBotic Ultimate SR04", + "url":"/pkg/SGBotic/pxt-SGBotic-Ultimate-SR04-RGB", + "cardType": "package" +}, { + "name": "TCS3200 Color sensor", + "url":"/pkg/joy-it/pxt-SEN-Color", + "cardType": "package" +}, { + "name": "MPU6050 Gyroscope", + "url":"/pkg/joy-it/SEN-MPU6050", + "cardType": "package" +}, { + "name": "DS18B20", + "url":"/pkg/bsiever/microbit-dstemp", + "cardType": "package" +}, { + "name": "DS18B20 two wire", + "url":"/pkg/bsiever/microbit-dstemp-2wire", + "cardType": "package" +}, { + "name": "DS3231", + "url":"/pkg/keble6/pxt-DS3231", + "cardType": "package" +}, { + "name": "Let's Talk Science COZIR sensor", + "url":"/pkg/letstalkscience/pxt-cozir", + "cardType": "package" +}, { + "name": "MAX6675", + "url":"/pkg/microsoft/pxt-max6675", + "cardType": "package" +}, { + "name": "Sonar", + "url":"/pkg/microsoft/pxt-sonar", + "cardType": "package" +}, { + "name": "Non-blocking Ultrasonic Sensing", + "url":"/pkg/1010Technologies/pxt-makerbit-ultrasonic", + "cardType": "package" +}, { + "name": "HX711 Weight Sensor", + "url":"/pkg/daferdur/pxt-myHX711", + "cardType": "package" +}, { + "name": "Bluetooth Temperature Sensor", + "url":"/pkg/microsoft/pxt-bluetooth-temperature-sensor", + "cardType": "package" +}, { + "name": "Bluetooth MAX6675", + "url":"/pkg/microsoft/pxt-bluetooth-max6675", + "cardType": "package" +}, { + "name": "ky040 rotary", + "url":"/pkg/Tinkertanker/pxt-rotary-encoder-ky040", + "cardType": "package" +}, { + "name": "GY521", + "url":"/pkg/PaulDFoster/pxt-microbit-GY521", + "cardType": "package" +}, { + "name": "DHT11 & DHT22 Temperature and Humidity", + "url":"/pkg/alankrantas/pxt-DHT11_DHT22", + "cardType": "package" +}, { + "name": "gator:light Light sensor", + "url":"/pkg/sparkfun/pxt-gator-light", + "cardType": "package" +}, { + "name": "gator:temp Temperature Sensor", + "url":"/pkg/sparkfun/pxt-gator-temp", + "cardType": "package" +}, { + "name": "gator:microphone Microphone", + "url":"/pkg/sparkfun/pxt-gator-microphone", + "cardType": "package" +}, { + "name": "gator:soil Soil Sensor", + "url":"/pkg/sparkfun/pxt-gator-soil", + "cardType": "package" +}, { + "name": "gator:temp Particle Sensor", + "url":"/pkg/sparkfun/pxt-gator-particle", + "cardType": "package" +}, { + "name": "MonkMakes Sensor", + "url":"/pkg/monkmakes/pxt-sensor", + "cardType": "package" +}, { + "name": "ALS Robot Electromagnet", + "url":"/pkg/alsrobot-microbit-makecode-packages/ALSRobotElectromagnet", + "cardType": "package" +}, { + "name": "MakerBit Touch MPR121", + "url": "/pkg/1010Technologies/pxt-makerbit-touch", + "cardType": "package" +}, { + "name": "Keyestudio Infrared Receiver", + "url": "/pkg/1010Technologies/pxt-makerbit-ir-receiver", + "cardType": "package" +}, { + "name": "BMP280 Barrometer", + "url": "/pkg/rebeccaclavier/pxt-bmp280", + "cardType": "package" +}, { + "name": "STTS751 temperature Sensor", + "url": "/pkg/makecode-extensions/STTS751", + "cardType": "package" +}, { + "name": "LSM6DSO Accelerometer/Gyroscope", + "url": "/pkg/makecode-extensions/LSM6DSO", + "cardType": "package" +}, { + "name": "LPS22 Pressure", + "url": "/pkg/makecode-extensions/LPS22", + "cardType": "package" +}, { + "name": "LIS2DW12 motion sensor", + "url": "/pkg/makecode-extensions/LIS2DW12", + "cardType": "package" +}, { + "name": "LIS2MDL magnetic sensor", + "url": "/pkg/makecode-extensions/LIS2MDL", + "cardType": "package" +}, { + "name": "HTS221 Humidity and temperature", + "url": "/pkg/makecode-extensions/HTS221", + "cardType": "package" +}, { + "name": "gator:UV UV Light sensor", + "url":"/pkg/sparkfun/pxt-gator-UV", + "cardType": "package" +}, { + "name": "Sensirion SEN55 (Air Quality) Sensor", + "url":"/pkg/bsiever/pxt-sen55", + "cardType": "package" +}] +``` + +## IoT + +```codecard +[{ + "name": "Smarthon IoT:bit", + "url":"/pkg/SMARTHON/pxt-iot-bit", + "cardType": "package" +}, { + "name": "DFRobot IoT Cloud Kit", + "url":"/pkg/DFRobot/pxt-DFRobot_IoT_Cloud_Kit", + "cardType": "package" +}, { + "name": "iClass IoT", + "url":"/pkg/KelieLeung/pxt-iClassIoT", + "cardType": "package" +}, { + "name": "Kittenbot Wifi", + "url":"/pkg/KittenBot/pxt-kittenwifi", + "cardType": "package" +}, { + "name": "ESP8266 AT", + "url":"/pkg/CytronTechnologies/pxt-esp8266", + "cardType": "package" +}, { + "name": "Wappsto:bit", + "url":"/pkg/Wappsto/pxt-wappsto", + "cardType": "package" +}, { + "name": "Hardwario IoT Kit", + "url":"/pkg/hardwario/pxt-microbit-hardwario", + "cardType": "package" +}, { + "name": "Pi Supply Lora Node", + "url":"/pkg/PiSupply/pxt-iot-lora-node", + "cardType": "package" +}, { + "name": "WiFi:Bit", + "url":"/pkg/SolderedElectronics/pxt-wifi", + "cardType": "package" +}, { + "name": "ESP8266 and ThingSpeak", + "url":"/pkg/alankrantas/pxt-ESP8266_ThingSpeak", + "cardType": "package" +}, { + "name": "DFRobot microIoT board", + "url":"/pkg/DFRobot/pxt-DFRobot-microIoT", + "cardType": "package" +}, { + "name": "Muselab WiFi IoT Shield", + "url":"/pkg/MUSELAB/pxt-wifi-shield", + "cardType": "package" +}] +``` + +## Kits + +```codecard +[{ + "name": "FWD Edu OpenSciEd Kit", + "url": "/pkg/Forward-Education/pxt-openscied", + "cardType": "package" +}, { + "name": "FWD Edu Coding For Good Kit", + "url": "/pkg/Forward-Education/pxt-coding-for-good", + "cardType": "package" +}, { + "name": "Smarthon Smart Home IoT Maker Kit", + "url": "/pkg/SMARTHON/pxt-smarthome", + "cardType": "package" +}, { + "name": "FWD Edu Smart Solder Kit", + "url": "/pkg/Forward-Education/pxt-smart-soldering", + "cardType": "package" +}, { + "name": "FWD Edu Smart Solar Kit", + "url": "/pkg/Forward-Education/pxt-smart-solar", + "cardType": "package" +}, { + "name": "FWD Edu Smart Hydroponics Kit", + "url": "/pkg/Forward-Education/pxt-smart-hydroponics", + "cardType": "package" +}, { + "name": "FWD Edu Smart: All Kits", + "url": "/pkg/Forward-Education/pxt-all-fwd-blocks", + "cardType": "package" +}, { + "name": "BP Lab micro:bit Kit", + "url": "/pkg/team-bp/pxt-bplab", + "cardType": "package" +}, { + "name": "Smarthon Smart City", + "url": "/pkg/SMARTHON/pxt-smartcity", + "cardType": "package" +}, { + "name": "HacKids hack:bit", + "url": "/pkg/HackidsEdu/pxt-hackbit", + "cardType": "package" +}, { + "name": "KittenBot Sugar", + "url": "/pkg/KittenBot/pxt-sugar", + "cardType": "package" +}, { + "name": "KittenBot Powerbrick", + "url": "/pkg/KittenBot/pxt-powerbrick", + "cardType": "package" +}, { + "name": "Kitronik LAB:bit", + "url": "/pkg/KitronikLtd/pxt-kitronik-lab-bit", + "cardType": "package" +}, { + "name": "PT-BOT PTKidsBIT", + "url": "/pkg/iBuilds/pxt-PTKidsBIT", + "cardType": "package" +}, { + "name": "Stemhub City", + "url": "/pkg/stemhub/pxt-StemhubCity", + "cardType": "package" +}, { + "name": "Tinkercademy Tinker:Kit", + "url": "/pkg/Tinkertanker/pxt-tinkercademy-tinker-kit", + "cardType": "package" +}, { + "name": "Freenove Starter Kit", + "url": "/pkg/Freenove/Makecode-Extension-Starter-Kit", + "cardType": "package" +}, { + "name": "Elecfreaks PlanetX sensor kit", + "url":"/pkg/elecfreaks/pxt-PlanetX", + "cardType": "package" +}, { + "name": "Inksmith Climate Action Kit: Land", + "url":"/pkg/climate-action-kits/pxt-climate-action-kit-land", + "cardType": "package" +}, { + "name": "Inksmith Climate Action Kit: Energy", + "url":"/pkg/climate-action-kits/pxt-climate-action-kit-land", + "cardType": "package" +}, { + "name": "Grove inventor kit", + "url":"/pkg/Seeed-Studio/pxt-grove", + "cardType": "package" +}, { + "name": "Minode Kit", + "url":"/pkg/minodekit/pxt-minode", + "cardType": "package" +}, { + "name": "DFRobot Boson Kit", + "url":"/pkg/dfrobot/pxt-dfrobot_bosonkit", + "cardType": "package" +}, { + "name": "Joy-IT Joy-Pi Advanced", + "url":"/pkg/joy-it/pxt-RB-JoyPi-Advanced", + "cardType": "package" +}, { + "name": "FWD Edu Climate Action Kit", + "url": "/pkg/Forward-Education/pxt-climate-action", + "cardType": "package" +}, { + "name": "FWD Edu Climate Action Kit Gen. 2 Kit", + "url":"/pkg/climate-action-kits/pxt-fwd-edu", + "cardType": "package" +}] +``` + +## LEDs and lights + +```codecard +[{ + "name": "NeoPixel Extended", + "url":"/pkg/PasAlt/pxt-neopixel-matrix-extension", + "cardType": "package" +}, { + "name": "ZHAW Luma Matrix", + "url":"/pkg/InES-HPMM/pxt-luma-matrix", + "cardType": "package" +}, { + "name": "Kitronik Lamp:Bit", + "url":"/pkg/KitronikLtd/pxt-kitronik-lampbit", + "cardType": "package" +}, { + "name": "Kitronik Halo HD", + "url":"/pkg/KitronikLtd/pxt-kitronik-halohd", + "cardType": "package" +}, { + "name": "NeoPixel", + "url":"/pkg/microsoft/pxt-neopixel", + "cardType": "package" +}, { + "name": "WS2812B", + "url": "/pkg/microsoft/pxt-ws2812b", + "cardType": "package" +}, { + "name": "4tronix Cube:Bit", + "url":"/pkg/4tronix/cubebit", + "cardType": "package" +}, { + "name": "51bit ColorBit", + "url":"/pkg/51bit/ColorBit", + "cardType": "package" +}, { + "name": "Kitronik Zip Tile", + "url":"/pkg/KitronikLtd/pxt-kitronik-zip-tile", + "cardType": "package" +}, { + "name": "MAX7219 8x8", + "url":"/pkg/alankrantas/pxt-MAX7219_8x8", + "cardType": "package" +}] +``` + +## Machine learning + +```codecard +[{ + "name": "FWD Vision AI Kit", + "url":"/pkg/Forward-Education/pxt-ai-vision", + "cardType": "package" +}, { + "name": "FWD Voice AI Kit", + "url":"/pkg/Forward-Education/pxt-ai-voice", + "cardType": "package" +}, { + "name": "enorasisCore", + "url":"/pkg/skinformatics/enorasisCore-makecode", + "cardType": "package" +}, { + "name": "Kocoafab COCOCAM", + "url":"/pkg/ekkai/aicococam", + "cardType": "package" +}, { + "name": "KittenBot KOI2 AI module", + "url":"/pkg/KittenBot/pxt-koi2", + "cardType": "package" +}, { + "name": "KittenBot KOI AI module", + "url":"/pkg/KittenBot/pxt-koi", + "cardType": "package" +}, { + "name": "Elecfreaks Smart AI Lens", + "url":"/pkg/elecfreaks/pxt-PlanetX-AI", + "cardType": "package" +}, { + "name": "MU Vision camera", + "url":"/pkg/mu-opensource/pxt-muvision", + "cardType": "package" +}, { + "name": "DFRobot HuskyLens", + "url":"/pkg/DFRobot/pxt-DFRobot_HuskyLens", + "cardType": "package" +}, { + "name": "DFRobot HuskyLens 2", + "url":"/pkg/DFRobot/pxt-DFRobot_HuskyLensV2", + "cardType": "package" +}] +``` +## Robotics + +```codecard +[{ + "name": "Peanut King micro:bit Shield V2", + "url":"/pkg/peanut-king-solution/pxt-pks-shield-v2", + "cardType": "package" +}, { + "name": "Peanut King Controller", + "url":"/pkg/peanut-king-solution/pxt-pks-controller", + "cardType": "package" +}, { + "name": "RobotGyms Robot PU Pro", + "url":"/pkg/robotgyms/pxt-robotpu", + "cardType": "package" +}, { + "name": "Elecfreaks PU Robot", + "url":"/pkg/elecfreaks/pxt-PU-Robot", + "cardType": "package" +}, { + "name": "Kitronik Design & Automate Accessory Kit", + "url":"/pkg/KitronikLtd/pxt-design-and-automate-accessory-kit", + "cardType": "package" +}, { + "name": "PyoBot", + "url":"/pkg/pyocodingcompany-crypto/pyobot-makecode", + "cardType": "package" +}, { + "name": "Tobbie-II (Translated)", + "url":"/pkg/Jim-no-surname-provided/pxt-tobbieII", + "cardType": "package" +}, { + "name": "Siyeenove Pybit", + "url":"/pkg/siyeenove/pxt_pybit", + "cardType": "package" +}, { + "name": "Siyeenove mShield", + "url":"/pkg/siyeenove/pxt_mshield", + "cardType": "package" +}, { + "name": "BrailleBot", + "url":"/pkg/roborisen/braillebot", + "cardType": "package" +}, { + "name": "Kitronik Mai-Z the Mouse Bot", + "url":"/pkg/KitronikLtd/pxt-kitronik-mai-z", + "cardType": "package" +}, { + "name": "DFRobot Creative Robotics Kit", + "url":"/pkg/DFRobot/pxt-DFRobot_creative-robotics-kit", + "cardType": "package" +}, { + "name": "Elecfreaks XGO Rider", + "url":"/pkg/elecfreaks/XGO-Rider", + "cardType": "package" +}, { + "name": "SIYEENOVE mCar", + "url":"/pkg/siyeenove/pxt_mcar", + "cardType": "package" +}, { + "name": "Cytron SUMO:BIT", + "url":"/pkg/CytronTechnologies/pxt-sumobit", + "cardType": "package" +}, { + "name": "PARALLAX cyber:bot", + "url":"/pkg/parallaxinc/cyberbot_makecode", + "cardType": "package" +}, { + "name": "Kitronik Craft & Code", + "url":"/pkg/KitronikLtd/pxt-kitronik-Craft-and-Code", + "cardType": "package" +}, { + "name": "Lectrify Brick:Bit", + "url":"/pkg/softsmyth/lectrify-b4k", + "cardType": "package" +}, { + "name": "KittenBot TabbyBot", + "url":"/pkg/KittenBot/pxt-tabbyrobot", + "cardType": "package" +}, { + "name": "Gcube", + "url":"/pkg/roborisen/gcube", + "cardType": "package" +}, { + "name": "Roversa", + "url":"/pkg/eb8ga/pxt-roversa-2", + "cardType": "package" +}, { + "name": "4tronix M.A.R.S. Rover", + "url":"/pkg/4tronix/mars-rover", + "cardType": "package" +}, { + "name": "Cytron MOTION:BIT", + "url":"/pkg/CytronTechnologies/pxt-motionbit", + "cardType": "package" +}, { + "name": "MAKE&LEARN Didacbot", + "url":"/pkg/MakeAndLearn/pxt-didacbot", + "cardType": "package" +}, { + "name": "Resolute Apprentice Car", + "url":"/pkg/resolute-support/pxt-apprentice_Car", + "cardType": "package" +}, { + "name": "Elecfreaks XGO", + "url":"/pkg/elecfreaks/pxt-xgo", + "cardType": "package" +}, { + "name": "Robotixlab Theta", + "url":"/pkg/4tronix/Theta", + "cardType": "package" +}, { + "name": "Kitronik :CREATE Simple Servo Control Board", + "url":"/pkg/KitronikLtd/pxt-kitronik-simple-servo", + "cardType": "package" +}, { + "name": "Kittenbot miniLFR", + "url":"/pkg/KittenBot/pxt-minilfr", + "cardType": "package" +}, { + "name": "Cytron ZOOM:BIT", + "url":"/pkg/CytronTechnologies/pxt-zoombit", + "cardType": "package" +}, { + "name": "Kid Spark Spark:bit", + "url":"/pkg/KidSpark/pxt-sparkbit", + "cardType": "package" +}, { + "name": "BPI TriodeCar", + "url":"/pkg/BPI-STEAM/pxt-triodecar", + "cardType": "package" +}, { + "name": "ArtecRobo Kit", + "url":"/pkg/artec-kk/pxt-artecrobo-kit", + "cardType": "package" +}, { + "name": "Elecfreaks DRONE:BIT", + "url":"/pkg/elecfreaks/pxt-Dronebit/", + "cardType": "package" +}, { + "name": "MakeKit Hoverbit", + "url":"/pkg/gomakekit/Hoverbit_V2", + "cardType": "package" +}, { + "name": "Stemhubbit car", + "url":"/pkg/stemhub/pxt-Stemhubbit", + "cardType": "package" +}, { + "name": "MATRIX Micro", + "url":"/pkg/matrix-robotics/pxt-MatrixMicro", + "cardType": "package" +}, { + "name": "PTKidsBIT", + "url":"/pkg/iBuilds/pxt-PTKidsBIT-Robot", + "cardType": "package" +}, { + "name": "Finch 2.0", + "url":"/pkg/BirdBrainTechnologies/pxt-finch", + "cardType": "package" +}, { + "name": "Bouw je BEP", + "url":"/pkg/Bouw-je-BEP/Bouw-je-BEP", + "cardType": "package" +}, { + "name": "DF Robot Maqueen Plus", + "url":"/pkg/DFRobot/pxt-DFRobot-Maqueenplus", + "cardType": "package" +}, { + "name": "DF Robot Maqueen Plus V2", + "url":"/pkg/DFRobot/pxt-DFRobot_MaqueenPlus_v20", + "cardType": "package" +}, { + "name": "Joy IT Joy Car", + "url":"/pkg/joy-it/Joy-Car", + "cardType": "package" +}, { + "name": "Kitronik :MOVE Motor", + "url":"/pkg/KitronikLtd/pxt-kitronik-move-motor", + "cardType": "package" +}, { + "name": "A4 Technologies CODO", + "url":"/pkg/CODOmicrobit/pxt-CODO", + "cardType": "package" +}, { + "name": "Strawbees Robotic Inventions Kit", + "url":"/pkg/strawbees/pxt-robotic-inventions", + "cardType": "package" +}, { + "name": "Kitronik :MOVE mini", + "url":"/pkg/KitronikLtd/pxt-kitronik-servo-lite", + "cardType": "package" +}, { + "name": "Kitronik Integrated Robotics Board", + "url":"/pkg/KitronikLtd/pxt-kitronik-robotics-board", + "cardType": "package" +}, { + "name": "Kitronik Motor Driver Board", + "url":"/pkg/KitronikLtd/pxt-kitronik-motor-driver", + "cardType": "package" +}, { + "name": "Kitronik 16 Servo Board", + "url":"/pkg/KitronikLtd/pxt-kitronik-i2c-16-servo", + "cardType": "package" +}, { + "name": "YFROBOT Valon", + "url":"/pkg/YFROBOT-TM/pxt-yfrobot-valon", + "cardType": "package" +}, { + "name": "4tronix BitBot", + "url":"/pkg/4tronix/BitBot", + "cardType": "package" +},{ + "name": "4tronix Orbit", + "url":"/pkg/4tronix/Orbit", + "cardType": "package" +}, { + "name": "4tronix Drive:Bit", + "url":"/pkg/4tronix/DriveBit", + "cardType": "package" +}, { + "name": "4tronix Servo:Bit", + "url":"/pkg/4tronix/ServoBit", + "cardType": "package" +}, { + "name": "4tronix MiniBit", + "url":"/pkg/4tronix/MiniBit", + "cardType": "package" +}, { + "name": "Elecfreaks TPBot", + "url":"/pkg/elecfreaks/pxt-TPBot", + "cardType": "package" +}, { + "name": "DF Robot Maqueen", + "url":"/pkg/DFRobot/pxt-maqueen", + "cardType": "package" +}, { + "name": "Sunfounder Sloth", + "url":"/pkg/sunfounder/pxt-sloth", + "cardType": "package" +}, { + "name": "Sphero RVR", + "url":"/pkg/sphero-inc/sphero-sdk-microbit-makecode", + "cardType": "package" +}, { + "name": "Sparkfun Moto:bit", + "url":"/pkg/sparkfun/pxt-moto-bit", + "cardType": "package" +}, { + "name": "EBOTICS MIBO", + "url":"/pkg/EBOTICS/pxt-eboticsMIBO", + "cardType": "package" +}, { + "name": "ALSRobot MinCruise", + "url":"/pkg/alsrobot-microbit-makecode-packages/MiniCruise", + "cardType": "package" +}, { + "name": "ReroKit rero:micro", + "url":"/pkg/ReRoKit/pxt-reromicro", + "cardType": "package" +}, { + "name": "PLEN bit full", + "url":"/pkg/plenprojectcompany/pxt-PLENbit_full", + "cardType": "package" +}, { + "name": "PLEN bit", + "url":"/pkg/plenprojectcompany/pxt-PLENbit", + "cardType": "package" +}, { + "name": "UCL Junk Robot", + "url":"/pkg/chevyng/pxt-ucl-junkrobot", + "cardType": "package" +}, { + "name": "Elecfreaks Cutebot", + "url":"/pkg/elecfreaks/pxt-cutebot", + "cardType": "package" +}, { + "name": "Elecfreaks Cutebot Pro", + "url":"/pkg/elecfreaks/pxt-cutebot-pro", + "cardType": "package" +}, { + "name": "Kittenbot RobotBit", + "url":"/pkg/kittenbot/pxt-robotbit", + "cardType": "package" +}, { + "name": "inex iBit", + "url":"/pkg/emwta/pxt-iBit", + "cardType": "package" +}, { + "name": "InkSmith k8 robotics kit", + "url":"/pkg/k8robotics/pxt-k8", + "cardType": "package" +}, { + "name": "Freenove Micro:Rover", + "url":"/pkg/Freenove/Makecode-Extension-Rover", + "cardType": "package" +}, { + "name": "Gigglebot", + "url":"/pkg/dexterind/pxt-giggle", + "cardType": "package" +}, { + "name": "Robobit", + "url":"/pkg/4tronix/Robobit", + "cardType": "package" +}, { + "name": "Pi Supply Bit:Buggy", + "url":"/pkg/PiSupply/pxt-bitbuggy", + "cardType": "package" +}, { + "name": "ALS Robot Coo Coo", + "url":"/pkg/alsrobot-microbit-makecode-packages/CooCoo", + "cardType": "package" +}, { + "name": "ALS Robot CruiseBit", + "url":"/pkg/alsrobot-microbit-makecode-packages/CruiseBit", + "cardType": "package" +}, { + "name": "Hummingbird Bit", + "url":"/pkg/BirdBrainTechnologies/pxt-hummingbird-bit", + "cardType": "package" +}, { + "name": "Inex iKB-1 controller board", + "url":"/pkg/jcubuntu/pxt-iKB1", + "cardType": "package" +}, { + "name": "MakerBit motor controller", + "url":"/pkg/1010Technologies/pxt-makerbit-motor", + "cardType": "package" +}, { + "name": "mikRobot", + "url":"/pkg/KS-Bulme/pxt-mikRobot", + "cardType": "package" +}, { + "name": "Tobbie II", + "url":"/pkg/kaku111/pxt-tobbieII", + "cardType": "package" +}, { + "name": "Kitronik ACCESS:bit", + "url":"/pkg/KitronikLtd/pxt-kitronik-accessbit", + "cardType": "package" +}, { + "name": "Kitronik Fischertechnik interface", + "url":"/pkg/KitronikLtd/pxt-kitronik-fischertechnik", + "cardType": "package" +}, { + "name": "Keigan Motor", + "url": "/pkg/keigan-motor/pxt-KeiganMotor", + "cardType": "package" +}, { + "name": "TCEA Nexus:bit and Nexusbot", + "url":"/pkg/beyond-coding-tw/pxt-nexusbot", + "cardType": "package" +}, { + "name": "Kitronik Klip Motor", + "url":"/pkg/KitronikLtd/pxt-kitronik-klip-motor", + "cardType": "package" +}, { + "name": "Keyestudio Robot Car", + "url":"/pkg/Veilkrand/pxt-RobotCar", + "cardType": "package" +}, { + "name": "TinkerTanker Stepper Motor", + "url":"/pkg/Tinkertanker/pxt-stepper-motor", + "cardType": "package" +}, { + "name": "ALS Robot Keyboard", + "url":"/pkg/alsrobot-microbit-makecode-packages/ALSRobotKeyboard", + "cardType": "package" +}, { + "name": "Elecfreaks NeZha", + "url": "/pkg/elecfreaks/pxt-nezha", + "cardType": "package" +}, { + "name": "Elecfreaks NeZha V2", + "url": "/pkg/elecfreaks/pxt-nezha2", + "cardType": "package" +}] +``` + +## Sensor boards + +```codecard +[{ + "name": "FWD Edu UBit", + "url": "/pkg/Forward-Education/pxt-fwd-ubit", + "cardType": "package" +}, { + "name": "Ceibal Ubit", + "url": "/pkg/Forward-Education/pxt-ceibal-ubit", + "cardType": "package" +}, { + "name": "BestModules BMduino", + "url": "/pkg/BestModules-Libraries/pxt-bmduino", + "cardType": "package" +}, { + "name": "Backyard Brains Spiker:Bit", + "url": "/pkg/BackyardBrains/pxt-spikerbit", + "cardType": "package" +}, { + "name": "Elecfreaks Petal:bit", + "url": "/pkg/elecfreaks/pxt-petal", + "cardType": "package" +}, { + "name": "Joy IT RFID Module MFRC-522", + "url": "/pkg/joy-it/pxt-rfid-mfrc522", + "cardType": "package" +}, { + "name": "Joy-IT ADS1115", + "url":"/pkg/joy-it/pxt-ads1115", + "cardType": "package" +}, { + "name": "DFRobot Environment Science Board ", + "url":"/pkg/DFRobot/pxt-DFRobot_Environment_Science", + "cardType": "package" +}, { + "name": "PT-BOT KidsBIT", + "url":"/pkg/iBuilds/pxt-PTKidsBIT-IoT", + "cardType": "package" +}, { + "name": "Kitronik Air Quality & Environmental Board", + "url":"/pkg/KitronikLtd/pxt-kitronik-air-quality", + "cardType": "package" +}, { + "name": "DFRobot Natural Science Board", + "url":"/pkg/DFRobot/pxt-DFRobot-NaturalScience", + "cardType": "package" +}, { + "name": "Kitronik Klimate Board", + "url":"/pkg/KitronikLtd/pxt-kitronik-klimate", + "cardType": "package" +}, { + "name": "Kitronik Smart Greenhouse", + "url":"/pkg/KitronikLtd/pxt-kitronik-smart-greenhouse", + "cardType": "package" +}, { + "name": "Make&Learn micro:shield", + "url":"/pkg/MakeAndLearn/pxt-microshield", + "cardType": "package" +}, { + "name": "Sparkfun Weather:bit", + "url":"/pkg/sparkfun/pxt-weather-bit", + "cardType": "package" +}, { + "name": "Sparkfun gator:environment", + "url":"/pkg/sparkfun/pxt-gator-environment", + "cardType": "package" +}, { + "name": "XinaBox SW01 Advanced Weather Sensor", + "url":"/pkg/xinabox/pxt-SW01", + "cardType": "package" +}, { + "name": "Cytron Edubit", + "url":"/pkg/CytronTechnologies/pxt-edubit", + "cardType": "package" +}, { + "name": "Cytron Rekabit", + "url":"/pkg/CytronTechnologies/pxt-rekabit", + "cardType": "package" +}, { + "name": "Cytron Rekabit RBT Project Kit", + "url":"/pkg/CytronTechnologies/pxt-rekabit-rbt-project-kit", + "cardType": "package" +}, { + "name": "Imagimaker Magisheild", + "url":"/pkg/Imagimaker/pxt-imagimaker", + "cardType": "package" +}, { + "name": "Kitronik clip detector", + "url": "/pkg/KitronikLtd/pxt-kitronik-clip-detector", + "cardType": "package" +}, { + "name": "Pimoroni Envirobit", + "url": "/pkg/pimoroni/pxt-envirobit", + "cardType": "package" +}, { + "name": "Pimoroni Automationbit", + "url":"/pkg/pimoroni/pxt-automationbit", + "cardType": "package" +}, { + "name": "51bit Smart Tools", + "url": "/pkg/51bit/SmartTools", + "cardType": "package" +}, { + "name": "MakerBit", + "url": "/pkg/1010Technologies/pxt-makerbit", + "cardType": "package" +}, { + "name": "MakerBit Pins", + "url": "/pkg/1010Technologies/pxt-makerbit-pins", + "cardType": "package" +}, { + "name": "Elecfreaks Wukon", + "url": "/pkg/elecfreaks/pxt-wukong", + "cardType": "package" +}, { + "name": "Elite Longanbit", + "url": "/pkg/longan-link/pxt-longanbit", + "cardType": "package" +}, { + "name": "Adafruit Crickit", + "url": "/pkg/adafruit/pxt-crickit", + "cardType": "package" +}, { + "name": "Adafruit Seesaw", + "url": "/pkg/adafruit/pxt-seesaw", + "cardType": "package" +}] +``` + +## Sound + +```codecard +[{ + "name": "Sonification", + "url":"/pkg/davidnsousa/sonification", + "cardType": "package" +}, { + "name": "Kitronik Klef Piano", + "url":"/pkg/KitronikLtd/pxt-kitronik-klef-piano", + "cardType": "package" +}, { + "name": "Catalex Serial MP3 Player v1.0", + "url": "/pkg/1010Technologies/pxt-makerbit-mp3", + "cardType": "package" +}, { + "name": "51bit DFPlayer mini", + "url":"/pkg/51bit/dfplayermini", + "cardType": "package" +}] +``` + +## Wearables + +```codecard +[{ + "name": "4tronix EggBit", + "url":"/pkg/4tronix/EggBit", + "cardType": "package" +}, { + "name": "Bright Wearables Bright Board", + "url":"/pkg/BrightWearables/pxt-microbit-brightboard", + "cardType": "package" +}] +``` + +## Utilities +```codecard +[{ + "name": "My Controller", + "url": "/pkg/aorczyk/my-controller", + "cardType": "package" +}, { + "name": "States", + "url": "/pkg/hovavo/pxt-states", + "cardType": "package" +}, { + "name": "Hebrew", + "url": "/pkg/shahart/heb-microbit", + "cardType": "package" +}, { + "name": "Faces", + "url": "/pkg/GrandpaBond/pxt-faces", + "cardType": "package" +}, { + "name": "Makey Makey Code-a-Key", + "url": "/pkg/joylabz/code-a-key-extension", + "cardType": "package" +}, { + "name": "FlexFX", + "url": "/pkg/GrandpaBond/pxt-flexfx", + "cardType": "package" +}, { + "name": "Meter", + "url": "/pkg/GrandpaBond/pxt-meter", + "cardType": "package" +}, { + "name": "Morse Code", + "url": "/pkg/bsiever/pxt-morse", + "cardType": "package" +}, { + "name": "Button clicks", + "url": "/pkg/bsiever/microbit-pxt-clicks", + "cardType": "package" +}, { + "name": "Rotate Display", + "url":"/pkg/bsiever/microbit-pxt-rotate", + "cardType": "package" +}, { + "name": "Bluetooth HID", + "url":"/pkg/bsiever/microbit-pxt-blehid", + "cardType": "package" +}, { + "name": "Soroban abacus", + "url":"/pkg/aorczyk/soroban", + "cardType": "package" +}, { + "name": "Lego PF recorder", + "url":"/pkg/aorczyk/pf-recorder", + "cardType": "package" +}, { + "name": "Lego PF transmitter", + "url":"/pkg/aorczyk/lego-pf-transmitter", + "cardType": "package" +}, { + "name": "Lego PF receiver", + "url":"/pkg/aorczyk/lego-pf-receiver", + "cardType": "package" +}, { + "name": "Kodely dot", + "url":"/pkg/Kodely-io/dot", + "cardType": "package" +}, { + "name": "Wait until...", + "url":"/pkg/TeacherPinky/Wait-Until-Blocks", + "cardType": "package" +}, { + "name": "micro:bit power saving", + "url":"/pkg/microbit-foundation/pxt-microbit-v2-power", + "cardType": "package" +}, { + "name": "Sound Level in decibels (dB)", + "url":"/pkg/microbit-foundation/pxt-sound-level-db", + "cardType": "package" +}, { + "name": "DS3231 Real Time Clock", + "url":"/pkg/AlexandreFrolov/DS3231", + "cardType": "package" +}, { + "name": "Time & Date", + "url":"/pkg/bsiever/microbit-pxt-timeanddate", + "cardType": "package" +}, { + "name": "Kitronik Realtime Clock", + "url":"/pkg/KitronikLtd/pxt-kitronik-rtc", + "cardType": "package" +}, { + "name": "Code Dojo Olney", + "url":"/pkg/CoderDojoOlney/pxt-olney", + "cardType": "package" +}, { + "name": "Inventura textbook", + "url":"/pkg/assirati/pxt-inventura", + "cardType": "package" +}, { + "name": "micro:turtle", + "url":"/pkg/microsoft/pxt-microturtle", + "cardType": "package" +}, { + "name": "MIDI", + "url":"/pkg/microsoft/pxt-midi", + "cardType": "package" +}, { + "name": "Bluetooth MIDI", + "url":"/pkg/microsoft/pxt-bluetooth-midi", + "cardType": "package" +}, { + "name": "BlockyTalkyBLE", + "url":"/pkg/LaboratoryForPlayfulComputation/pxt-BlockyTalkyBLE", + "cardType": "package" +}, { + "name": "Katakana", + "url":"/pkg/mbitfun/pxt-katakana", + "cardType": "package" +}, { + "name": "LINE BLE beacon", + "url":"/pkg/pizayanz/pxt-linebeacon", + "cardType": "package" +}, { + "name": "Pimoroni Scrollbit", + "url":"/pkg/pimoroni/pxt-scrollbit", + "cardType": "package" +}, { + "name": "SBRICK", + "url":"/pkg/vengit/pxt-sbrick", + "cardType": "package" +}, { + "name": "Annikken Andee", + "url":"/pkg/Annikken/pxt-Andee", + "cardType": "package" +}, { + "name": "Proportional Font", + "url":"/pkg/lwchkg/pxt-proportional-font", + "cardType": "package" +}] +``` diff --git a/docs/hero-banner.md b/docs/hero-banner.md new file mode 100644 index 00000000000..8bd562fd6c8 --- /dev/null +++ b/docs/hero-banner.md @@ -0,0 +1,35 @@ +# Hero Banner + +Here are some cool activities to get you started with your @boardname@! + +## Intro Content + +### ~ codecard +* name: Intro to micro:bit +* description: Introduction to the BBC micro:bit +* imageUrl: /static/herogallery/intro-to-microbit.png +* url: https://microbit.org/get-started/first-steps/introduction/ +* cardType: link +--- +* name: Behind the MakeCode Hardware +* description: Behind the MakeCode Hardware +* imageUrl: /static/herogallery/behind-makecode-hardware.png +* youTubePlaylistId: PLMMBk9hE-SeqDYtw9pGNPsQ10V_EGMyGe +--- +* name: Fun with Radio +* description: Send messages with your micro:bit +* imageUrl: /static/herogallery/send-messages-radio.png +* url: /projects/micro-chat +* cardType: tutorial +--- +* name: Soil Moisture Project +* description: Track the soil moisture of your plants! +* imageUrl: /static/herogallery/soil-moisture.png +* url: /projects/soil-moisture +--- +* name: micro:bit CreateAI +* description: micro:bit CreateAI +* imageUrl: /static/herogallery/microbit-createai.png +* url: https://createai.microbit.org/ +* cardType: link +### ~ diff --git a/docs/homepage-content.md b/docs/homepage-content.md new file mode 100644 index 00000000000..7e3a9da8252 --- /dev/null +++ b/docs/homepage-content.md @@ -0,0 +1,66 @@ +# Adding content to the home page + +The Editor home page contains galleries of card links to projects, tutorials, lessons, videos, and other content sources. Many content items featured on the home page are hosted at the editor's website while some other items are from external sources. + +Much of content available on the home page is developed or selected by the MakeCode Team. Content from other contributors is welcomed though and can be featured in the home page galleries. + +![Home page content example](/static/mb/homepage-content-example.jpg) + +## Content requirements + +* Your content should be open source and free to use without any restrictions other than author attribution (you may specify an open source license the provides for such usage if you wish). +* The instructions should be well written and easy to follow. +* It should not include any personally identifiable information beyond the author's name (authors can remain anonymous too). +* Content will be reviewed and approved by the MakeCode Team. + +## What you need to submit + +To list a content item on the home page, you need to provide: + +1. A name or title for the content item. +2. A brief one or two sentence description of what your content is or does (preferably in English for localization and is 30 words or less). +3. A thumbnail image that represents your content. Size the image to approximately **300 x 200** pixels. +4. A full URL for the item. For a tutorial, a complete tutorial URL is needed (see https://makecode.com/writing-docs/user-tutorials for more details). + +Here is the content link information mapped to a home page card example: + +![Home page link example](/static/mb/homepage-link-example.jpg) + +1. Name or Title +2. Description +3. Thumbnail image (JPG or PNG) +4. Content link URL (not shown on the home page card) + +## Submission process + +Create and submit a [GitHub issue](@githubUrl@/issues) requesting your content to be listed. In the issue description, include the name or title of the content item, description, content URL, and copy in the thumbnail image. GitHub will automatically create an image URL for your thumbnail when you paste or drag your image in. Your issue description will look something like this when you write it: + +``` +Request to include a user content source on the editor home page. The content is a simple tutorial on how the create exploding sprites in Arcade. + +## Home page link info + +### Name + +Exploding Sprites + +### Description + +Learn how to create moving sprites that randomly explode! + +### Content Link + +https://arcade.makecode.com/#tutorial:https://makecode.com/_rewr9iop + +### Thumbnail + +![image](https://github.com/microsoft/pxt/assets/27789908/f5c21294-1145-4010-8de0-560b8afbfeec) +``` + +## Approval #target-approval + +For approval to add items to the home page, please follow the **[instructions](https://support.microbit.org/support/solutions/articles/19000054952-makecode-extension-and-tutorial-approval)** for submitting content to the Micro:bit Foundation. + +## Featured content + +To request featured space in the Home Page banner carousel, the requirements are similar to gallery items except for the image size. We require a link, a short description, and a "Hero" image that is **2100 x 500** pixels. Please note that banner space is very limited and approval for items included there is very selective. \ No newline at end of file diff --git a/docs/index-ref.json b/docs/index-ref.json index 5dc0b3b0fc7..e5f58002aa7 100644 --- a/docs/index-ref.json +++ b/docs/index-ref.json @@ -1,3 +1,3 @@ { - "appref": "v3.0.66" + "appref": "v9.0.12" } diff --git a/docs/jacdac.md b/docs/jacdac.md new file mode 100644 index 00000000000..61219a733ad --- /dev/null +++ b/docs/jacdac.md @@ -0,0 +1,59 @@ +# Jacdac + +Connect and Code. Instantly. + +## Getting started + +[Jacdac](https://aka.ms/jacdac) is a plug-and-play hardware accessory system that provides simulation and physical device twin in MakeCode. + +```codecard +[{ + "name": "Getting started", + "description": "Connect and Code. Instantly in MakeCode.", + "url":"https://jacdac.github.io/jacdac-docs/clients/makecode/", + "imageUrl": "/static/jacdac/getting-started.jpg" +}] +``` + +## Projects + +```codecard +[ + { + "name": "Button smasher", + "description": "How many times can you smash the button in 10 seconds?", + "url": "https://jacdac.github.io/jacdac-docs/clients/makecode/projects/button-smasher/", + "imageUrl": "/static/jacdac/button-smasher.jpg" + }, + { + "name": "Slider Sound Bender", + "description": "Create twisted sounds using a slider module.", + "url": "https://jacdac.github.io/jacdac-docs/clients/makecode/projects/slider-sound-bender/", + "imageUrl": "/static/jacdac/slider-sound-bender.jpg" + }, + { + "name": "Light Sound Bender", + "description": "Create twisted sounds using light levels", + "url": "https://jacdac.github.io/jacdac-docs/clients/makecode/projects/light-sound-bender/", + "imageUrl": "/static/jacdac/light-sound-bender.jpg" + }, + { + "name": "Rotary Sound Bender", + "description": "Create twisted sounds using a rotary encoder module.", + "url": "https://jacdac.github.io/jacdac-docs/clients/makecode/projects/rotary-sound-bender/", + "imageUrl": "/static/jacdac/rotary-sound-bender.jpg" + }, + { + "name": "Sound LED", + "description": "Show the sound level on a LED ring", + "url": "https://jacdac.github.io/jacdac-docs/clients/makecode/projects/sound-led/", + "imageUrl": "/static/jacdac/sound-led.jpg" + }, + { + "name": "Magnetic Sound Bender", + "description": "Create twisted sounds using a magnet", + "url": "https://jacdac.github.io/jacdac-docs/clients/makecode/projects/magnetic-sound-bender/", + "imageUrl": "/static/jacdac/magnetic-sound-bender.jpg" + } +] +``` diff --git a/docs/lessons/digi-yoyo/activity.md b/docs/lessons/digi-yoyo/activity.md index 4e15adf28bc..168000f4a3e 100644 --- a/docs/lessons/digi-yoyo/activity.md +++ b/docs/lessons/digi-yoyo/activity.md @@ -40,7 +40,7 @@ let count = 0 while (count < 10) { basic.pause(100) basic.showNumber(count) - count = count + (count - 1) + count = count + 1 } ``` diff --git a/docs/microbit-org/createai.md b/docs/microbit-org/createai.md new file mode 100644 index 00000000000..40f6bc23c64 --- /dev/null +++ b/docs/microbit-org/createai.md @@ -0,0 +1,41 @@ +# CreateAI + +Projects to get learners started quickly with AI and machine learning on the micro:bit. + +## Projects + +```codecard +[{ + "name": "AI storytelling friend", + "description": "Use storytelling to introduce AI.", + "url": "https://microbit.org/projects/make-it-code-it/ai-storytelling-friend/", + "imageUrl":"/static/microbit-org/createai/storytelling-friend.png", + "label": " ", + "labelClass": "black microbit-ribbon large", + "cardType": "link" +}, { + "name": "Simple AI exercise timer", + "description": "Make a smart exercise timer using AI.", + "url": "https://microbit.org/projects/make-it-code-it/simple-ai-exercise-timer/", + "imageUrl":"/static/microbit-org/createai/simple-exercise-timer.png", + "label": " ", + "labelClass": "black microbit-ribbon large", + "cardType": "link" +}, { + "name": "AI activity timer", + "description": "Use AI to detect and time specific activities.", + "url": "https://microbit.org/projects/make-it-code-it/ai-activity-timer/", + "imageUrl":"/static/microbit-org/createai/activity-timer.png", + "label": " ", + "labelClass": "black microbit-ribbon large", + "cardType": "link" +}, { + "name": "More about CreateAI", + "description": "Explore AI on and offscreen with tools, resources and more.", + "url": "https://microbit.org/createai/", + "imageUrl":"/static/microbit-org/createai/more-about.png", + "label": " ", + "labelClass": "black microbit-ribbon large", + "cardType": "link" +}] +``` diff --git a/docs/microbit-org/data-logging.md b/docs/microbit-org/data-logging.md new file mode 100644 index 00000000000..f02bea93b3f --- /dev/null +++ b/docs/microbit-org/data-logging.md @@ -0,0 +1,49 @@ +# Data Logging Examples + +Use the micro:bit’s data logging feature in science and other experiments. + +## Projects + +```codecard +[{ + "name": "Traffic survey data logger", + "description": "Survey traffic, wildlife or anything around you!", + "url": "https://makecode.microbit.org/_gh4CetMLC5i4", + "imageUrl": "/static/microbit-org/data-logging/traffic-survey.png", + "label": " ", + "labelClass": "black microbit-ribbon large", + "cardType": "sharedExample" +}, { + "name": "Kick strength data logger", + "description": "Use data science to improve your sports skills.", + "url": "https://makecode.microbit.org/_drsVdM9dccxq", + "imageUrl": "/static/microbit-org/data-logging/kick-strength.png", + "label": " ", + "labelClass": "black microbit-ribbon large", + "cardType": "sharedExample" +}, { + "name": "Environment data logger", + "description": "Record and study data about the world around you.", + "url": "https://makecode.microbit.org/_WbKetCEgVDX2", + "imageUrl": "/static/microbit-org/data-logging/environment.png", + "label": " ", + "labelClass": "black microbit-ribbon large", + "cardType": "sharedExample" +}, { + "name": "Solar panel experiment", + "description": "Decide where to put a solar panel with your micro:bit.", + "url": "https://makecode.microbit.org/_7L8hXcRUDCPF", + "imageUrl": "/static/microbit-org/data-logging/solar-panel.png", + "label": " ", + "labelClass": "black microbit-ribbon large", + "cardType": "sharedExample" +}, { + "name": "Movement data logger", + "description": "Use data logging to make a better step counter.", + "url": "https://makecode.microbit.org/_6ftECdEohfpb", + "imageUrl": "/static/microbit-org/data-logging/movement.png", + "label": " ", + "labelClass": "black microbit-ribbon large", + "cardType": "sharedExample" +}] +``` diff --git a/docs/microbit-org/feature-videos.md b/docs/microbit-org/feature-videos.md new file mode 100644 index 00000000000..6d249a30353 --- /dev/null +++ b/docs/microbit-org/feature-videos.md @@ -0,0 +1,71 @@ +# Introductory micro:bit Feature Videos + +Short animated videos to share with learners. + +## Videos + +```codecard +[{ + "name": "Introduction to the BBC micro:bit", + "description": "Meet the BBC micro:bit.", + "label": " ", + "labelClass": "black microbit-ribbon large", + "youTubeId": "u2u7UJSRuko", + "youTubePlaylistId": "PLEo0hMrjdofusveMscRFN9FeqKzDBzuXr", + "imageUrl": "/static/microbit-org/feature-videos/introduction.png" +}, +{ + "name": "Input and output devices", + "description": "How the BBC micro:bit helps you understand computer input and output devices.", + "label": " ", + "labelClass": "black microbit-ribbon large", + "youTubeId": "NkoS2JXaBuM", + "youTubePlaylistId": "PLEo0hMrjdofusveMscRFN9FeqKzDBzuXr", + "imageUrl": "/static/microbit-org/feature-videos/input-output.png" +}, +{ + "name": "Processor", + "description": "The processor is the most important part of your BBC micro:bit - watch this video to discover why they are essential parts of any computer, phone or tablet.", + "label": " ", + "labelClass": "black microbit-ribbon large", + "youTubeId": "Y9tk07CzTAA", + "youTubePlaylistId": "PLEo0hMrjdofusveMscRFN9FeqKzDBzuXr", + "imageUrl": "/static/microbit-org/feature-videos/processor.png" +}, +{ + "name": "LEDs", + "description": "LED lights are perfect for the BBC micro:bit - watch this video to find out why.", + "label": " ", + "labelClass": "black microbit-ribbon large", + "youTubeId": "eRhlaXqT-0w", + "youTubePlaylistId": "PLEo0hMrjdofusveMscRFN9FeqKzDBzuXr", + "imageUrl": "/static/microbit-org/feature-videos/leds.png" +}, +{ + "name": "Buttons", + "description": "The buttons are probably the first input device you'll use on BBC micro:bit - this video tells you how you can use them. ", + "label": " ", + "labelClass": "black microbit-ribbon large", + "youTubeId": "hnT0qHM3_hQ", + "youTubePlaylistId": "PLEo0hMrjdofusveMscRFN9FeqKzDBzuXr", + "imageUrl": "/static/microbit-org/feature-videos/buttons.png" +}, +{ + "name": "Accelerometer", + "description": "Like a phone, the micro:bit can sense movement using its accelerometer. This video tells you more about how you can use it.", + "label": " ", + "labelClass": "black microbit-ribbon large", + "youTubeId": "UT35ODxvmS0", + "youTubePlaylistId": "PLEo0hMrjdofusveMscRFN9FeqKzDBzuXr", + "imageUrl": "/static/microbit-org/feature-videos/accelerometer.png" +}, +{ + "name": "Full playlist", + "description": "Watch videos on all the features of the micro:bit.", + "label": " ", + "labelClass": "black microbit-ribbon large", + "url": "https://www.youtube.com/playlist?list=PLEo0hMrjdofusveMscRFN9FeqKzDBzuXr", + "youTubePlaylistId": "PLEo0hMrjdofusveMscRFN9FeqKzDBzuXr", + "imageUrl": "/static/microbit-org/feature-videos/full-playlist.png" +}] +``` diff --git a/docs/microbit-org/first-lessons.md b/docs/microbit-org/first-lessons.md new file mode 100644 index 00000000000..e651ca63bd9 --- /dev/null +++ b/docs/microbit-org/first-lessons.md @@ -0,0 +1,65 @@ +# First Lessons with MakeCode and the micro:bit + +Six projects featured in our starter [lessons](https://microbit.org/teach/lessons/first-lessons-with-makecode-and-the-microbit/) and companion [PD course](https://microbit.thinkific.com/courses/first-lessons-with-makecode-and-the-micro-bit). + +## Lessons + +```codecard +[{ + "name": "First lessons overview", + "description": "A sequence of lessons from the Micro:bit Educational Foundation that provide a pathway through six projects, ideal for getting started with the micro:bit", + "url":"https://microbit.org/teach/lessons/first-lessons-with-makecode-and-the-microbit", + "imageUrl": "/static/microbit-org/first-lessons/overview.png", + "label": " ", + "labelClass": "black microbit-ribbon large", + "cardType": "link" +}, { + "name": "Name badge", + "description": "Students create their first programs and transfer them to their micro:bits.", + "url": "https://microbit.org/teach/lessons/name-badge/", + "imageUrl": "/static/microbit-org/first-lessons/name-badge.png", + "label": " ", + "labelClass": "black microbit-ribbon large", + "cardType": "link" +}, { + "name": "Beating heart", + "description": "Create a simple animation to learn about sequence and simple loops.", + "url": "https://microbit.org/teach/lessons/beating-heart/", + "imageUrl": "/static/microbit-org/first-lessons/beating-heart.png", + "label": " ", + "labelClass": "black microbit-ribbon large", + "cardType": "link" +}, { + "name": "Emotion badge", + "description": "Start learning about inputs and outputs using buttons and icons on the display.", + "url": "https://microbit.org/teach/lessons/emotion-badge/", + "imageUrl": "/static/microbit-org/first-lessons/emotion-badge.png", + "label": " ", + "labelClass": "black microbit-ribbon large", + "cardType": "link" +}, { + "name": "Step counter", + "description": "Introduce variables to track your step count and begin to use the accelerometer input.", + "url": "https://microbit.org/teach/lessons/step-counter/", + "imageUrl": "/static/microbit-org/first-lessons/step-counter.png", + "label": " ", + "labelClass": "black microbit-ribbon large", + "cardType": "link" +}, { + "name": "Nightlight", + "description": "Make an automatic nightlight and discover how logic, conditionals and inputs and outputs combine to make a simple control system.", + "url": "https://microbit.org/teach/lessons/nightlight/", + "imageUrl": "/static/microbit-org/first-lessons/nightlight.png", + "label": " ", + "labelClass": "black microbit-ribbon large", + "cardType": "link" +}, { + "name": "Rock, paper, scissors", + "description": "Combine skills from the previous lessons to turn your micro:bit into an electronic simulation of a popular game of chance.", + "url": "https://microbit.org/teach/lessons/rock-paper-scissors/", + "imageUrl": "/static/microbit-org/first-lessons/rock-paper-scissors.png", + "label": " ", + "labelClass": "black microbit-ribbon large", + "cardType": "link" +}] +``` diff --git a/docs/microbit-org/make-it-code-it.md b/docs/microbit-org/make-it-code-it.md new file mode 100644 index 00000000000..f04ec78b340 --- /dev/null +++ b/docs/microbit-org/make-it-code-it.md @@ -0,0 +1,57 @@ +# Make it: code it Examples + +Projects you can try out straight away or code from scratch. + +## Projects + +```codecard +[{ + "name": "Dance steps", + "description": "Use loops to help create a dance routine.", + "url": "https://makecode.microbit.org/_edAayo1kC04Y", + "imageUrl": "/static/microbit-org/make-it-code-it/dance-steps.png", + "label": " ", + "labelClass": "black microbit-ribbon large", + "cardType": "sharedExample" +}, { + "name": "Poetry generator", + "description": "Generate random phrases to use in a poem.", + "url": "https://makecode.microbit.org/_D2fETcEuMCvX", + "imageUrl": "/static/microbit-org/make-it-code-it/poetry-generator.png", + "label": " ", + "labelClass": "black microbit-ribbon large", + "cardType": "sharedExample" +}, { + "name": "Activity picker", + "description": "Can't agree on what to do? Let your micro:bit decide!", + "url": "https://makecode.microbit.org/_gXM8uh850CCe", + "imageUrl": "/static/microbit-org/make-it-code-it/activity-picker.png", + "label": " ", + "labelClass": "black microbit-ribbon large", + "cardType": "sharedExample" +}, { + "name": "Calming LEDs", + "description": "Regulate your breathing and relax.", + "url": "https://makecode.microbit.org/_baC2XD9E1aW7", + "imageUrl": "/static/microbit-org/make-it-code-it/calming-leds.png", + "label": " ", + "labelClass": "black microbit-ribbon large", + "cardType": "sharedExample" +}, { + "name": "Funny voice recorder", + "description": "Turn your micro:bit into a voice changer.", + "url": "https://makecode.microbit.org/_9TY7rdVAV5Fd", + "imageUrl": "/static/microbit-org/make-it-code-it/funny-voice.png", + "label": " ", + "labelClass": "black microbit-ribbon large", + "cardType": "sharedExample" +}, { + "name": "Distance calculator", + "description": "Use your micro:bit to measure distances.", + "url": "https://makecode.microbit.org/_DACivW4iMe0t", + "imageUrl": "/static/microbit-org/make-it-code-it/distance-calculator.png", + "label": " ", + "labelClass": "black microbit-ribbon large", + "cardType": "sharedExample" +}] +``` diff --git a/docs/microbit-org/professional-development.md b/docs/microbit-org/professional-development.md new file mode 100644 index 00000000000..5bfbbeb5a5b --- /dev/null +++ b/docs/microbit-org/professional-development.md @@ -0,0 +1,65 @@ +# Educator Professional Development + +Professional development courses from the Micro:bit Educational Foundation. + +## Courses + +```codecard +[{ + "name": "First lessons with MakeCode and the micro:bit", + "description": "Introduces a sequence of six projects that are perfect to introduce your learners to coding on the micro:bit using Microsoft MakeCode.", + "url": "https://microbit.thinkific.com/courses/first-lessons-with-makecode-and-the-micro-bit", + "imageUrl": "/static/microbit-org/professional-development/first-lessons.png", + "label": " ", + "labelClass": "black microbit-ribbon large", + "cardType": "link" +}, { + "name": "Gesture and movement", + "description": "Explores how to use the micro:bit’s accelerometer sensor in code. Using built-in gesture recognition, you’ll make projects that respond when you shake the micro:bit or rotate it in different directions like a phone or tablet screen.", + "url": "https://microbit.thinkific.com/courses/gesture-and-movement", + "imageUrl": "/static/microbit-org/professional-development/gesture-movement.png", + "label": " ", + "labelClass": "black microbit-ribbon large", + "cardType": "link" +}, { + "name": "Science exploration with the micro:bit ", + "description": "Introduces how to use the BBC micro:bit as an effective tool to support hands-on science investigations in your classroom. It highlights four engaging, practical science investigations that you can take back to your classroom.", + "url": "https://microbit.thinkific.com/courses/science-exploration-with-the-micro-bit", + "imageUrl": "/static/microbit-org/professional-development/science-exploration.png", + "label": " ", + "labelClass": "black microbit-ribbon large", + "cardType": "link" +}, { + "name": "Making and sensing sound", + "description": "Explore music and creative sound-making with the micro:bit. We’ll also show how you can create code that uses the micro:bit V2’s built-in microphone to respond to and measure sound.", + "url": "https://microbit.thinkific.com/courses/sensing-and-making-sound", + "imageUrl": "/static/microbit-org/professional-development/sensing-making-sound.png", + "label": " ", + "labelClass": "black microbit-ribbon large", + "cardType": "link" +}, { + "name": "Introducing loops", + "description": "Uses the micro:bit to show how loops repeat sets of instructions to make your code do more. We explain the difference between infinite and numbered loops with practical examples.", + "url": "https://microbit.thinkific.com/courses/introducing-loops", + "imageUrl": "/static/microbit-org/professional-development/introducing-loops.png", + "label": " ", + "labelClass": "black microbit-ribbon large", + "cardType": "link" +}, { + "name": "Practical tips for teachers", + "description": "Introduces the basics of what you need to get started teaching with the micro:bit, some practical suggestions and top tips for getting the most from your lesson time.", + "url": "https://microbit.thinkific.com/courses/practical-tips", + "imageUrl": "/static/microbit-org/professional-development/practical-tips.png", + "label": " ", + "labelClass": "black microbit-ribbon large", + "cardType": "link" +}, { + "name": "All courses", + "description": "All professional development courses from the Micro:bit Educational Foundation.", + "url": "https://microbit.thinkific.com/", + "imageUrl": "/static/microbit-org/professional-development/all-courses.png", + "label": " ", + "labelClass": "black microbit-ribbon large", + "cardType": "link" +}] +``` diff --git a/docs/microcode.md b/docs/microcode.md new file mode 100644 index 00000000000..0bc1b6e53b4 --- /dev/null +++ b/docs/microcode.md @@ -0,0 +1,22 @@ +# MicroCode + +[MicroCode](https://microbit-apps.github.io/microcode-classic/docs/manual) is an experimental tile-based language and editor for young coders and coders with disabilities on the BBC micro:bit V2 (V1 not supported), inspired by Kodu Game Lab. + +## Getting started + +```codecard +[{ + "name": "Getting Started", + "description": "MicroCode is an experimental tile-based language and editor for young coders and coders with disabilities on the BBC micro:bit V2 (V1 not supported), inspired by Kodu Game Lab.", + "url":"https://microbit-apps.github.io/microcode-classic/docs/manual", + "imageUrl": "/static/microcode/home.png" + }, + + { + "name": "Samples", + "description": "Annotated MicroCode programs with screen animations, sounds, radio messages and more.", + "url": "https://microbit-apps.github.io/microcode-classic/docs/samples", + "imageUrl": "/static/microcode/samples.png" + } +] +``` diff --git a/docs/projects.md b/docs/projects.md index b9dcc9938e5..3bad358d7ed 100644 --- a/docs/projects.md +++ b/docs/projects.md @@ -9,20 +9,35 @@ "largeImageUrl": "/static/mb/projects/flashing-heart/sim.gif" }, { - "name": "Live Coding", - "url": "/live-coding", - "imageUrl": "/static/live-coding/NvEOKZ8wh9s.jpg" + "name": "Tutorials for the new micro:bit (V2)", + "url": "/tutorials-v2", + "imageUrl": "/static/mb/projects/pet-hamster.png" }, { "name": "Games", "url": "/projects/games", "imageUrl": "/static/mb/projects/a4-motion.png" }, + { + "name": "Make it: code it Examples", + "url": "/microbit-org/make-it-code-it", + "imageUrl": "/static/microbit-org/make-it-code-it/dance-steps.png" + }, { "name": "Radio Games", "url": "/projects/radio-games", "imageUrl": "/static/multi.png" }, + { + "name": "Data Logging Examples", + "url": "/microbit-org/data-logging", + "imageUrl": "/static/microbit-org/data-logging/traffic-survey.png" + }, + { + "name": "Live Coding", + "url": "/live-coding", + "imageUrl": "/static/live-coding/NvEOKZ8wh9s.jpg" + }, { "name": "Fashion", "url": "/projects/fashion", @@ -54,15 +69,40 @@ "imageUrl": "/static/mb/projects/turtle-square.png" }, { - "name": "Blocks To JavaScript", + "name": "Blocks to JavaScript", "url": "/courses/blocks-to-javascript", "imageUrl": "/static/courses/blocks-to-javascript/hello-javascript.png" }, + { + "name": "First Lessons with MakeCode and the micro:bit", + "url": "/microbit-org/first-lessons", + "imageUrl": "/static/microbit-org/first-lessons/overview.png" + }, + { + "name": "CreateAI", + "url": "/microbit-org/createai", + "imageUrl": "/static/microbit-org/createai/storytelling-friend.png" + }, { "name": "Courses", "url": "/courses", "imageUrl": "/static/courses/csintro.jpg" }, + { + "name": "Jacdac", + "url": "/jacdac", + "imageUrl": "/static/jacdac/getting-started.jpg" + }, + { + "name": "MicroCode for the new micro:bit (V2)", + "url": "/microcode", + "imageUrl": "/static/microcode/home.png" + }, + { + "name": "Introductory micro:bit Feature Videos", + "url": "/microbit-org/feature-videos", + "imageUrl": "/static/microbit-org/feature-videos/introduction.png" + }, { "name": "Behind the MakeCode Hardware", "url": "/behind-the-makecode-hardware", @@ -73,6 +113,11 @@ "url": "/science-experiments", "imageUrl": "/static/mb/science-experiments/data-collection.jpg" }, + { + "name": "Educator Professional Development", + "url": "/microbit-org/professional-development", + "imageUrl": "/static/microbit-org/professional-development/first-lessons.png" + }, { "name": "Coding for Teachers", "url": "/coding-for-teachers", @@ -94,19 +139,28 @@ ## See Also [Tutorials](/tutorials), -[Live Coding](/live-coding), +[Tutorials for the new micro:bit (V2)](/tutorials-v2), [Games](/projects/games), +[Make it: code it Examples](/microbit-org/make-it-code-it), [Radio Games](/projects/radio-games), +[Data Logging Examples](/microbit-org/data-logging), +[Live Coding](/live-coding), [Fashion](/projects/fashion), [Music](/projects/music), [Toys](/projects/toys), [Science](/projects/science), [Tools](/projects/tools), [Turtle](/projects/turtle), -[Blocks To JavaScript](/courses/blocks-to-javascript), +[Blocks to JavaScript](/courses/blocks-to-javascript), +[First Lessons with MakeCode and the micro:bit](/microbit-org/first-lessons), +[CreateAI](/microbit-org/createai), [Courses](/courses), +[Jacdac](/jacdac), +[MicroCode for the new micro:bit (V2)](/microcode), +[Introductory micro:bit Feature Videos](/microbit-org/feature-videos), [Behind the MakeCode Hardware](/behind-the-makecode-hardware), [Science Experiments](/science-experiments), +[Educator Professional Development](/microbit-org/professional-development), [Coding for Teachers](/coding-for-teachers), [Coding Cards](/coding-cards), [Deep Dive](/deep-dive) diff --git a/docs/projects/7-seconds.md b/docs/projects/7-seconds.md index 87b19d33981..ecc0c970afd 100644 --- a/docs/projects/7-seconds.md +++ b/docs/projects/7-seconds.md @@ -1,16 +1,16 @@ # 7 seconds game -## Introduction @unplugged +## {Introduction @unplugged} The goal of this game is press a button after **exactly** 7 seconds! ![A micro:bit looking at a 7 second stopwatch](/static/mb/projects/7-seconds.png) -This game is inspired from the [flipping panckakes game](https://www.elecfreaks.com/store/blog/post/flipping-pancakes-microbit-game.html). +This game is inspired from the [flipping panckakes game](https://www.elecfreaks.com/blog/post/flipping-pancakes-microbit-game.html). -## Step 1 +## {Step 1} -The player starts the timer by pressing button **A**. Add the code to run code when ``||input:button A is pressed||``. +The player starts the timer by pressing button **A**. We'll run the code run code when ``||input:button A is pressed||``. ```blocks input.onButtonPressed(Button.A, function () { @@ -18,7 +18,7 @@ input.onButtonPressed(Button.A, function () { }) ``` -## Step 2 +## {Step 2} We need to remember the time when the button was pressed so that we can compute the elapsed time later on. Add code to store the ``||input:running time||`` in a ``||variables:start||`` variable. @@ -31,7 +31,7 @@ input.onButtonPressed(Button.A, function () { }) ``` -## Step 3 +## {Step 3} Show something on the screen so that the user knows that the timer has started... @@ -44,7 +44,7 @@ input.onButtonPressed(Button.A, function () { }) ``` -## Step 4 +## {Step 4} The player stops the timer by pressing button **B**. Add the code to run code when ``||input:button B is pressed||``. @@ -54,7 +54,7 @@ input.onButtonPressed(Button.B, function () { }) ``` -## Step 5 +## {Step 5} Compute the elapsed time as ``||input:running time||`` ``||math:minus||`` ``||variables:start||`` and store it into a new variable ``||variables:elapsed||``. @@ -67,7 +67,7 @@ input.onButtonPressed(Button.B, function () { }) ``` -## Step 6 +## {Step 6} Compute the ``||variables:score||`` of the game as the ``||math:absolute value||`` of the ``||math:difference||`` of ``||variables:elapsed||`` time from 7 seconds, which is 7000 milliseconds. @@ -82,7 +82,7 @@ input.onButtonPressed(Button.B, function () { }) ``` -## Step 7 +## {Step 7} Display the score on the screen and your game is ready! @@ -97,3 +97,7 @@ input.onButtonPressed(Button.B, function () { basic.showNumber(score) }) ``` + +```template +input.onButtonPressed(Button.A, function () {}) +``` diff --git a/docs/projects/SUMMARY.md b/docs/projects/SUMMARY.md index b645acfcac6..b779c0271f4 100644 --- a/docs/projects/SUMMARY.md +++ b/docs/projects/SUMMARY.md @@ -7,6 +7,53 @@ * [Dice](/projects/dice) * [Love Meter](/projects/love-meter) * [Micro Chat](/projects/micro-chat) +* [Tutorials for the new micro:bit (V2)](/tutorials-v2) + * [Pet Hamster](/projects/v2-pet-hamster) + * [Countdown](/projects/v2-countdown) + * [Morse Chat](/projects/v2-morse-chat) + * [Clap Lights](/projects/v2-clap-lights) + * [Blow Away](/projects/v2-blow-away) + * [Cat Napping](/projects/v2-cat-napping) +* [Games](/projects/games) + * [Rock Paper Scissors](/projects/rock-paper-scissors) + * [Rock Paper Scissors V2](/projects/rock-paper-scissors-v2) + * [Coin Flipper](/projects/coin-flipper) + * [7 seconds](/projects/7-seconds) + * [Hot Potato](/projects/hot-potato) + * [Heads Guess!](/projects/heads-guess) + * [Reaction Time](/projects/reaction-time) + * [Tug-Of-LED](/projects/tug-of-led) + * [Magic Button Trick](/projects/magic-button-trick) + * [Snap the dot](/projects/snap-the-dot) + * [Salute!](/projects/salute) + * [Karel the LED](/projects/karel) + * [Crashy bird](/projects/crashy-bird) +* [Make it: code it Examples](/microbit-org/make-it-code-it) + * [Dance steps](https://makecode.microbit.org/_edAayo1kC04Y) + * [Poetry generator](https://makecode.microbit.org/_D2fETcEuMCvX) + * [Activity picker](https://makecode.microbit.org/_gXM8uh850CCe) + * [Calming LEDs](https://makecode.microbit.org/_baC2XD9E1aW7) + * [Funny voice recorder](https://makecode.microbit.org/_9TY7rdVAV5Fd) + * [Distance calculator](https://makecode.microbit.org/_DACivW4iMe0t) +* [Radio Games](/projects/radio-games) + * [Multi Editors](https://makecode.microbit.org/---multi) + * [Multi Dice](/projects/multi-dice) + * [Mood Radio](/projects/mood-radio) + * [Tele-potato](/projects/tele-potato) + * [Fireflies](/projects/fireflies) + * [Hot or Cold](/projects/hot-or-cold-multi) + * [Red Light Green Light](/projects/red-light-green-light) + * [Voting Machine](/projects/voting-machine) + * [Rock Paper Scissors Teams](/projects/rps-teams) + * [Micro:Coin](/projects/micro-coin) + * [Infection](/projects/infection) + * [Best Friends](/projects/best-friends) +* [Data Logging Examples](/microbit-org/data-logging) + * [Traffic survey data logger](https://makecode.microbit.org/_gh4CetMLC5i4) + * [Kick strength data logger](https://makecode.microbit.org/_drsVdM9dccxq) + * [Environment data logger](https://makecode.microbit.org/_WbKetCEgVDX2) + * [Solar panel experiment](https://makecode.microbit.org/_7L8hXcRUDCPF) + * [Movement data logger](https://makecode.microbit.org/_6ftECdEohfpb) * [Live Coding](/live-coding) * [Flashing Heart](https://youtu.be/NvEOKZ8wh9s) * [Name Tag](https://youtu.be/xpRI5jjQ31E) @@ -41,32 +88,6 @@ * [Red Light Green Light](https://youtu.be/Cm22diu8CFA) * [Stopwatch progress](https://youtu.be/2aAcBP2xcaI) * [PlayList](https://www.youtube.com/playlist?list=PLMMBk9hE-SepocOwueEtTDyOPI_TBE9yC) -* [Games](/projects/games) - * [Rock Paper Scissors](/projects/rock-paper-scissors) - * [Coin Flipper](/projects/coin-flipper) - * [7 seconds](/projects/7-seconds) - * [Hot Potato](/projects/hot-potato) - * [Heads Guess!](/projects/heads-guess) - * [Reaction Time](/projects/reaction-time) - * [Tug-Of-LED](/projects/tug-of-led) - * [Magic Button Trick](/projects/magic-button-trick) - * [Snap the dot](/projects/snap-the-dot) - * [Salute!](/projects/salute) - * [Karel the LED](/projects/karel) - * [Crashy bird](/projects/crashy-bird) -* [Radio Games](/projects/radio-games) - * [Multi Editors](https://makecode.microbit.org/---multi) - * [Multi Dice](/projects/multi-dice) - * [Mood Radio](/projects/mood-radio) - * [Tele-potato](/projects/tele-potato) - * [Fireflies](/projects/fireflies) - * [Hot or Cold](/projects/hot-or-cold-multi) - * [Red Light Green Light](/projects/red-light-green-light) - * [Voting Machine](/projects/voting-machine) - * [Rock Paper Scissors Teams](/projects/rps-teams) - * [Micro:Coin](/projects/micro-coin) - * [Infection](/projects/infection) - * [Best Friends](/projects/best-friends) * [Fashion](/projects/fashion) * [Duct Tape Wallet](/projects/wallet) * [Watch](/projects/watch) @@ -77,11 +98,15 @@ * [Hack Your Headphones](/projects/hack-your-headphones) * [Banana Keyboard](/projects/banana-keyboard) * [Guitar](/projects/guitar) + * [Jonny's Bird](/projects/jonnys-bird) + * [Electric Guitar](/projects/electric-guitar) * [Toys](/projects/toys) * [Inchworm](/projects/inchworm) * [Milk Carton Robot](/projects/milk-carton-robot) * [Robot Unicorn](/projects/robot-unicorn) * [Ticklebot](https://www.jasmineflorentine.com/ticklebot) + * [Octobot](https://browndoggadgets.dozuki.com/Guide/Octobot/306) + * [Two Player Maze](https://tinker-club.blogspot.com/p/two-player-maze-game-for-microbit.html) * [Milky Monster](/projects/milky-monster) * [Railway Crossing](/projects/railway-crossing) * [Kitronik RC Car Hack](/projects/rc-car) @@ -104,7 +129,7 @@ * [Square](/projects/turtle-square) * [Spiral](/projects/turtle-spiral) * [Scanner](/projects/turtle-scanner) -* [Blocks To JavaScript](/courses/blocks-to-javascript) +* [Blocks to JavaScript](/courses/blocks-to-javascript) * [Hello JavaScript](/courses/blocks-to-javascript/hello-javascript) * [Starter Blocks](/courses/blocks-to-javascript/starter-blocks) * [Writing Code](/courses/blocks-to-javascript/writing-code) @@ -112,22 +137,60 @@ * [Conditional Loops](/courses/blocks-to-javascript/conditional-loops) * [Command Responder](/courses/blocks-to-javascript/command-responder) * [Writing Functions](/courses/blocks-to-javascript/writing-functions) +* [First Lessons with MakeCode and the micro:bit](/microbit-org/first-lessons) + * [First lessons overview](https://microbit.org/teach/lessons/first-lessons-with-makecode-and-the-microbit) + * [Name badge](https://microbit.org/teach/lessons/name-badge/) + * [Beating heart](https://microbit.org/teach/lessons/beating-heart/) + * [Emotion badge](https://microbit.org/teach/lessons/emotion-badge/) + * [Step counter](https://microbit.org/teach/lessons/step-counter/) + * [Nightlight](https://microbit.org/teach/lessons/nightlight/) + * [Rock, paper, scissors](https://microbit.org/teach/lessons/rock-paper-scissors/) +* [CreateAI](/microbit-org/createai) + * [AI storytelling friend](https://microbit.org/projects/make-it-code-it/ai-storytelling-friend/) + * [Simple AI exercise timer](https://microbit.org/projects/make-it-code-it/simple-ai-exercise-timer/) + * [AI activity timer](https://microbit.org/projects/make-it-code-it/ai-activity-timer/) + * [More about CreateAI](https://microbit.org/createai/) * [Courses](/courses) * [Intro to CS Online](/courses/csintro) * [Intro to CS Classroom](/courses/csintro-educator) * [Science Experiments](/courses/ucp-science) + * [Cyber Arcade: Programming and Making with micro:bit](https://makered.org/resources/cyber-arcade-programming-and-making-with-microbit/) * [Learn All About micro:bit](https://goo.gl/XTPYpP) * [Coding and Innovation](https://sites.google.com/view/utahcodingproject/microbits/coding-innovation) + * [micro:bit Starter Lessons](https://mrmorrison.co.uk/microbit/starter/) + * [micro:bit Beyond Basics](https://mrmorrison.co.uk/microbit/beyondbasics/) + * [micro:bit Data and Sustainability](https://mrmorrison.co.uk/microbit/datasustainability/) * [First Steps](https://microbit.org/get-started/first-steps/introduction/) * [Make it: code it](https://microbit.org/projects/make-it-code-it/) - * [Networking with the micro:bit](https://microbit.org/projects/make-it-code-it/) - * [SparkFun Videos](https://youtu.be/kaNtg1HGXbY?list=PLBcrWxTa5CS0mWJrytvii8aG5KUqMXvSk) + * [Networking with the micro:bit](https://www.digitaltechnologieshub.edu.au/search/networking-with-the-micro-bit/) + * [SparkFun Videos](https://youtu.be/kaNtg1HGXbY) * [Logic Lab](/courses/logic-lab) + * [CodeJoy Remote Robotics](https://www.codejoy.org) * [Blocks to JavaScript](/courses/blocks-to-javascript) * [SparkFun Inventor's Kit](https://learn.sparkfun.com/tutorials/sparkfun-inventors-kit-for-microbit-experiment-guide/introduction-to-the-sparkfun-inventors-kit-for-microbit) * [Kitronik Inventor Kit](https://www.kitronik.co.uk/blog/inventors-kit-experiment-1-help) * [micro:bit of Things](https://sites.google.com/view/microbitofthings) + * [ARM University - micro:course](https://github.com/arm-university/micro-course) * [A-Z Robotics](https://tinkerspark.teachable.com/) +* [Jacdac](/jacdac) + * [Getting started](https://jacdac.github.io/jacdac-docs/clients/makecode/) + * [Button smasher](https://jacdac.github.io/jacdac-docs/clients/makecode/projects/button-smasher/) + * [Slider Sound Bender](https://jacdac.github.io/jacdac-docs/clients/makecode/projects/slider-sound-bender/) + * [Light Sound Bender](https://jacdac.github.io/jacdac-docs/clients/makecode/projects/light-sound-bender/) + * [Rotary Sound Bender](https://jacdac.github.io/jacdac-docs/clients/makecode/projects/rotary-sound-bender/) + * [Sound LED](https://jacdac.github.io/jacdac-docs/clients/makecode/projects/sound-led/) + * [Magnetic Sound Bender](https://jacdac.github.io/jacdac-docs/clients/makecode/projects/magnetic-sound-bender/) +* [MicroCode for the new micro:bit (V2)](/microcode) + * [Getting Started](https://microbit-apps.github.io/microcode-classic/docs/manual) + * [Samples](https://microbit-apps.github.io/microcode-classic/docs/samples) +* [Introductory micro:bit Feature Videos](/microbit-org/feature-videos) + * [Introduction to the BBC micro:bit](https://youtu.be/u2u7UJSRuko) + * [Input and output devices](https://youtu.be/NkoS2JXaBuM) + * [Processor](https://youtu.be/Y9tk07CzTAA) + * [LEDs](https://youtu.be/eRhlaXqT-0w) + * [Buttons](https://youtu.be/hnT0qHM3_hQ) + * [Accelerometer](https://youtu.be/UT35ODxvmS0) + * [Full playlist](https://www.youtube.com/playlist?list=PLEo0hMrjdofusveMscRFN9FeqKzDBzuXr) * [Behind the MakeCode Hardware](/behind-the-makecode-hardware) * [LEDs](https://youtu.be/qqBmvHD5bCw) * [Buttons](https://youtu.be/t_Qujjd_38o) @@ -147,6 +210,14 @@ * [Egg Drop](https://youtu.be/tnDJFdC3Nd4) * [Battery Tester](https://youtu.be/gdlc34nhjK4) * [Rocket Acceleration](https://youtu.be/m9ntqxh8FvQ) +* [Educator Professional Development](/microbit-org/professional-development) + * [First lessons with MakeCode and the micro:bit](https://microbit.thinkific.com/courses/first-lessons-with-makecode-and-the-micro-bit) + * [Gesture and movement](https://microbit.thinkific.com/courses/gesture-and-movement) + * [Science exploration with the micro:bit ](https://microbit.thinkific.com/courses/science-exploration-with-the-micro-bit) + * [Making and sensing sound](https://microbit.thinkific.com/courses/sensing-and-making-sound) + * [Introducing loops](https://microbit.thinkific.com/courses/introducing-loops) + * [Practical tips for teachers](https://microbit.thinkific.com/courses/practical-tips) + * [All courses](https://microbit.thinkific.com/) * [Coding for Teachers](/coding-for-teachers) * [Part 1 - Introduction](https://youtu.be/hr8O_pslp8Q) * [Part 2 - Connect & Code](https://youtu.be/_cTHlQXwEO4) diff --git a/docs/projects/analog-pin-tester.md b/docs/projects/analog-pin-tester.md index d44946937ad..0dc46b58bea 100644 --- a/docs/projects/analog-pin-tester.md +++ b/docs/projects/analog-pin-tester.md @@ -5,7 +5,7 @@ Press ``A`` to scroll the value on the screen. ```blocks let reading = 0 -basic.forever(() => { +basic.forever(function () { reading = pins.analogReadPin(AnalogPin.P0) led.plotBarGraph( reading, diff --git a/docs/projects/banana-keyboard/code.md b/docs/projects/banana-keyboard/code.md index b4ae8122ca5..48d18c04944 100644 --- a/docs/projects/banana-keyboard/code.md +++ b/docs/projects/banana-keyboard/code.md @@ -7,43 +7,43 @@ Have you ever tried making beat box sounds? Let's try making a beatbox with code Start by adding a variable to store a musical note. Rename the variable to `sound`. Set the value of the variable to the note block `Middle A` from the **Music** drawer. ```blocks -let sound = music.noteFrequency(Note.A); +let sound = music.noteFrequency(Note.A) ``` We want to play music when the fruit connected to a pin pressed. So, we register an event handler that executes whenever pin **1** is pressed. Pin **1** is, of course, connected to the banana. Add a ``||input:on pin pressed||`` block from the **Input** drawer. ```blocks -let sound = music.noteFrequency(Note.A); -input.onPinPressed(TouchPin.P1, () => { +let sound = music.noteFrequency(Note.A) +input.onPinPressed(TouchPin.P1, function () { }) ``` -Now, let's create some notes to play when the banana is pressed. Click on the **Loops** drawer then insert a ``||loops:repeat||`` loop into the ``||input:on pin pressed||`` block. Click on the **Variables** drawer and pull out a ``||variables:change item by||`` block and put it into the loop. Rename the variable to `sound`. Change the value from `1` to `25`. This will increase the variable `sound` from the note frequency of block `Middle A` to `Middle A` plus 25 and so on. Put a ``||variables:set to||`` block for `sound` right after the loop. Set it to `Middle A` a in order to reset the sound after a banana press. +Now, let's create some notes to play when the banana is pressed. Click on the **Loops** drawer then insert a ``||loops:repeat||`` loop into the ``||input:on pin pressed||`` block. Click on the **Variables** drawer and pull out a ``||variables:change item by||`` block and put it into the loop. Rename the variable to `sound`. Change the value from `1` to `25`. This will increase the variable `sound` from the note frequency of block `Middle A` to `Middle A` plus 25 and so on. Put a ``||variables:set to||`` block for `sound` right after the loop. Set it to `Middle A` in order to reset the sound after a banana press. ```blocks -let sound = music.noteFrequency(Note.A); +let sound = music.noteFrequency(Note.A) -input.onPinPressed(TouchPin.P1, () => { +input.onPinPressed(TouchPin.P1, function () { for (let i = 0; i < 4; i++) { - sound += 25; + sound += 25 } - sound = music.noteFrequency(Note.A); -}); + sound = music.noteFrequency(Note.A) +}) ``` Finally, insert a ``||music:play tone||`` above the ``||variables:change by||``. Pull out the ``sound`` variable block and drop it in the note slot of ``||music:play tone||``. Change the beat fraction from `1` to `1/4`. ```blocks -let sound = music.noteFrequency(Note.A); +let sound = music.noteFrequency(Note.A) -input.onPinPressed(TouchPin.P1, () => { +input.onPinPressed(TouchPin.P1, function () { for (let i = 0; i < 4; i++) { - music.playTone(sound, music.beat(BeatFraction.Quarter)); - sound += 25; + music.playTone(sound, music.beat(BeatFraction.Quarter)) + sound += 25 } - sound = music.noteFrequency(Note.A); -}); + sound = music.noteFrequency(Note.A) +}) ``` Click `|Download|` and try a banana press. Did you hear 4 notes play? @@ -55,23 +55,23 @@ Go back to **[Make](/projects/banana-keyboard/make)** and repeat steps 7 and 8 w Duplicate the ``||input:on pin pressed||`` event handler to make a second one. For the new ``||input:on pin pressed||``, change the pin name to **P2**. In the pin **P2** event, let's have the the frequency in the variable `sound` decrease by 25 instead of having it increase. Change the `25` in the ``||variables:change by||`` block to `-25`. OK, your code now looks like this: ```blocks -let sound = music.noteFrequency(Note.A); +let sound = music.noteFrequency(Note.A) -input.onPinPressed(TouchPin.P1, () => { +input.onPinPressed(TouchPin.P1, function () { for (let i = 0; i < 4; i++) { - music.playTone(sound, music.beat(BeatFraction.Quarter)); - sound += 25; + music.playTone(sound, music.beat(BeatFraction.Quarter)) + sound += 25 } - sound = music.noteFrequency(Note.A); -}); + sound = music.noteFrequency(Note.A) +}) -input.onPinPressed(TouchPin.P2, () => { +input.onPinPressed(TouchPin.P2, function () { for (let i = 0; i < 4; i++) { - music.playTone(sound, music.beat(BeatFraction.Quarter)); - sound += -25; + music.playTone(sound, music.beat(BeatFraction.Quarter)) + sound += -25 } - sound = music.noteFrequency(Note.A); -}); + sound = music.noteFrequency(Note.A) +}) ``` Click `|Download|` again and play both bananas. It's a fruit jam session! diff --git a/docs/projects/banana-keyboard/make.md b/docs/projects/banana-keyboard/make.md index f07b692fd5e..951e95c32d7 100644 --- a/docs/projects/banana-keyboard/make.md +++ b/docs/projects/banana-keyboard/make.md @@ -73,7 +73,7 @@ Your banana keyboard is ready! Connect your @boardname@ to your computer using your USB cable and run this script: ```blocks -input.onPinPressed(TouchPin.P1, () => { +input.onPinPressed(TouchPin.P1, function () { music.playTone(music.noteFrequency(Note.C), music.beat(BeatFraction.Quarter)); }); ``` diff --git a/docs/projects/carnival/button-points.md b/docs/projects/carnival/button-points.md new file mode 100644 index 00000000000..b5a0257867d --- /dev/null +++ b/docs/projects/carnival/button-points.md @@ -0,0 +1,118 @@ +# Add Points with Buttons +### @explicitHints true + + +## Introduction @showdialog + +Let's add a point to your score when a button is pressed on the @boardname@! + +![A graphic depicting someone pressing a button](/static/mb/projects/points.png) + + +## {Step 2} + +We 'll start by adding code to the ``||input:on button pressed||``
+container already in the workspace. + +💡 _You can click the arrow next to ``||input:A||`` and change to another button if you prefer._ + +#### ~ tutorialhint +```blocks +input.onButtonPressed(Button.A, function() { }) +``` + +## {Step 3} + +Open the ``||variables:Variables||`` category
+and drag ``||variables:change [score] by [1]||``
+into the empty ``||input:on button [A] [pressed]||``. + +#### ~ tutorialhint +```blocks +input.onButtonPressed(Button.A, function() { + score += 1 + }) +``` + +## {Step 4} + +Update the LEDs after you change the score by opening the
+``||basic:Basic||`` category and dragging ``||basic:show number [score]||``
+into **the end** of the ``||input:on button [A] [pressed]||`` container already in the workspace. + +#### ~ tutorialhint +```blocks +pins.onPulsed(DigitalPin.P0, PulseValue.High, function () { + score += 1 + basic.showNumber(score) +}) +``` + + +## {Step 4} + +Click the A button in the simulator to give your code a try. + +You should see the score go up each time the button is pressed. + + +## {Step 5} + +**Add sound effects.** + +Open the ``||music:Music||`` category and
+drag ``||music:play [ã€°ī¸] [in background]||``
+into **the end** of the ``||input:on button [A] [pressed]||`` container in the workspace. + + +#### ~ tutorialhint +```blocks +pins.onPulsed(DigitalPin.P0, PulseValue.High, function () { + score += 1 + basic.showNumber(score) + music._playDefaultBackground(music.createSoundExpression(WaveShape.Square, 400, 600, 255, 0, 100, SoundExpressionEffect.Warble, InterpolationCurve.Linear), music.PlaybackMode.InBackground) +}) +``` + + +## {Step 6} + +**Test again by pressing A** + +Your program should play a sound and increase your points with each click. + +💡 _You may need to unmute the simulator to hear your music._ + + + +## {Step 7} + +If you have a @boardname@ connected, click ``|Download|`` and transfer your code. + +Now you're ready to attach your @boardname@ to your project and try it out! + + + +```blockconfig.global + music._playDefaultBackground(music.createSoundExpression(WaveShape.Square, 400, 600, 255, 0, 100, SoundExpressionEffect.Warble, InterpolationCurve.Linear), music.PlaybackMode.InBackground) + basic.showNumber(score) +``` + + +```template +input.onButtonPressed(Button.A, function() {}) + +let score = 0 +score = 0 +basic.showNumber(score) +``` + +```ghost +basic.showIcon(IconNames.Yes) + score += 1 + + +let score = 0 +basic.showNumber(score) + +``` \ No newline at end of file diff --git a/docs/projects/carnival/circuit-win.md b/docs/projects/carnival/circuit-win.md new file mode 100644 index 00000000000..407525d2c54 --- /dev/null +++ b/docs/projects/carnival/circuit-win.md @@ -0,0 +1,107 @@ +# Connect a Circuit to Win +### @explicitHints true + + +## Introduction @showdialog + +Let's detect a WIN when a circuit is completed on the @boardname@! + +![A graphic depicting a sad micro:bit after loss](/static/mb/projects/clap-lights.png) + + +## {Step 2} + +We 'll start by adding code to the ``||input:on pin [P0] [pressed]||``
+container already in the workspace. + +💡 _You can click the arrow next to ``||input:P0||`` and change it to another pin if you prefer._ + +#### ~ tutorialhint +```blocks +input.onPinPressed(TouchPin.P0, function () {}) +``` + +## {Step 3} + +Open the ``||basic:Basic||`` category
+and drag ``||basic:show string ["WIN!"]||``
+into the empty ``||input:on pin [P0] [pressed]||`` container to display a message. + +#### ~ tutorialhint +```blocks +input.onPinPressed(TouchPin.P0, function () { + basic.showString("WIN!") +}) + + +``` + +## {Step 4} + +Click the pin marked **0** in the simulator to give your code a try. + +![An image of the pin you should click on the micro:bit](/static/mb/projects/p0.png) + + +## {Step 5} + +**Add some drama with music.** + +Open the ``||music:Music||`` category and
+drag ``||music:play [melody] [in background]||``
+into **the top** of the ``||input:on pin [P0] [pressed]||`` container in the workspace. + + +#### ~ tutorialhint +```blocks +input.onPinPressed(TouchPin.P0, function () { + music._playDefaultBackground(music.builtInPlayableMelody(Melodies.Dadadadum), music.PlaybackMode.InBackground) + basic.showString("WIN!") +}) +``` + + +## {Step 6} + +**Test again by clicking P0 in the simulator** + +Your program should play a song and scroll the word "WIN!". + +💡 _You may need to unmute the simulator to hear your music._ + + + +## {Step 7} + +If you have a @boardname@ connected, click ``|Download|`` and transfer your code. + +Now you're ready to attach your @boardname@ to your project and try it out! + + +💡 _Note that the **pin pressed** block requires the pin to be pressed **and** released before it will trigger._ + + + + +```blockconfig.global + music.play(music.builtinPlayableSoundEffect(soundExpression.soaring), music.PlaybackMode.InBackground) + basic.showString("WIN!") +``` + + +```template +input.onPinPressed(TouchPin.P0, function () { }) +``` + +```ghost +basic.showIcon(IconNames.Yes) +input.onButtonPressed(Button.A, function () { + music.play(music.builtinPlayableSoundEffect(soundExpression.sad), music.PlaybackMode.InBackground) + music._playDefaultBackground(music.builtInPlayableMelody(Melodies.Funeral), music.PlaybackMode.InBackground) + basic.showString("Loss") +}) +input.onPinPressed(TouchPin.P0, function () { + music.play(music.builtinPlayableSoundEffect(soundExpression.sad), music.PlaybackMode.InBackground) + basic.showString("WIN!") +}) +``` \ No newline at end of file diff --git a/docs/projects/carnival/shake-lose.md b/docs/projects/carnival/shake-lose.md new file mode 100644 index 00000000000..a8caa7da30a --- /dev/null +++ b/docs/projects/carnival/shake-lose.md @@ -0,0 +1,97 @@ +# Shake or Fall to Lose +### @explicitHints true + +## Introduction @showdialog + +Let's detect a loss when the @boardname@ shakes or drops! + +![A graphic depicting a sad micro:bit after loss](/static/mb/projects/lose.png) + +## {Step 2} + +We 'll start by adding code to the ``||input:on shake||`` container already in the workspace. + +💡 _You can also click the arrow next to ``||input:shake||`` and change the action to ``||input:free fall||`` or another event in the library._ + +#### ~ tutorialhint +```blocks +input.onGesture(Gesture.Shake, function() { }) +``` + +## {Step 3} + +Open the ``||basic:Basic||`` category
+and drag ``||basic:show string ["Loss"]||``
+into the empty ``||input:on shake||`` container to display a message. + +#### ~ tutorialhint +```blocks +input.onGesture(Gesture.Shake, function() { + basic.showString("Loss") +}) + + +``` + +## {Step 4} + +Click the little white circle in the simulator next to **SHAKE** to give your code a try. + +![An image of the word SHAKE above the B button](/static/mb/projects/shake.png) + + +## {Step 5} + +**Add some drama with music.** + +Open the ``||music:Music||`` category and
+drag ``||music:play [melody] [in background]||``
+into **the top** of the ``||input:on shake||`` container in the workspace. + + +#### ~ tutorialhint +```blocks +input.onGesture(Gesture.Shake, function() { + music._playDefaultBackground(music.builtInPlayableMelody(Melodies.Dadadadum), music.PlaybackMode.InBackground) + basic.showString("Loss") +}) +``` + + +## {Step 6} + +**Test again by clicking âšŦSHAKE** + +Your program should play a song and scroll the word "Loss". + +💡 _You may need to unmute the simulator to hear your music._ + + + +## {Step 7} + +If you have a @boardname@ connected, click ``|Download|`` and transfer your code. + +Now you're ready to attach your @boardname@ to your project and try it out! + + + +```blockconfig.global + music._playDefaultBackground(music.builtInPlayableMelody(Melodies.Funeral), music.PlaybackMode.InBackground) + basic.showString("Loss") + basic.showIcon(IconNames.Yes) +``` + + +```template +input.onGesture(Gesture.Shake, function() {}) +``` + +```ghost +basic.showIcon(IconNames.Yes) +input.onButtonPressed(Button.A, function () { + music.play(music.builtinPlayableSoundEffect(soundExpression.sad), music.PlaybackMode.InBackground) + music._playDefaultBackground(music.builtInPlayableMelody(Melodies.Funeral), music.PlaybackMode.InBackground) + basic.showString("Loss") +}) +``` \ No newline at end of file diff --git a/docs/projects/coin-flipper.md b/docs/projects/coin-flipper.md index 7a84dafebf3..2d66ad4ca89 100644 --- a/docs/projects/coin-flipper.md +++ b/docs/projects/coin-flipper.md @@ -1,40 +1,40 @@ # Coin Flipper -## Introduction @unplugged +## {Introduction @unplugged} Let's create a coin flipping program to simulate a real coin toss. We'll use icon images to represent a ``heads`` or ``tails`` result. ![Simulating coin toss](/static/mb/projects/coin-flipper/coin-flipper.gif) -## Step 1 +## {Step 1} -Get an ``||input:on button A pressed||`` block from the ``||input:Input||`` drawer in the toolbox. We'll put our coin flipping code in here. +Let's start with the ``||input:on button A pressed||`` block on the Workspace. We'll put our coin flipping code in here. ```blocks -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function() { }) ``` -## Step 2 +## {Step 2} Grab an ``||logic:if else||`` block and set it inside ``||input:on button A pressed||``. Put a ``||Math:pick random true or false||`` into the ``||logic:if||`` as its condition. The ``||Math:pick random true or false||`` returns a random ``true`` or ``false`` value which we use to determine a ``heads`` or ``tails`` result for a coin toss. ```blocks -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function() { if (Math.randomBoolean()) { } else { } }) ``` -## Step 3 +## {Step 3} Now, put a ``||basic:show icon||`` block inside both the ``||logic:if||`` and the ``||logic:else||``. Pick images to mean ``heads`` and ``tails``. ```blocks -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function() { if (Math.randomBoolean()) { basic.showIcon(IconNames.Skull) } else { @@ -43,16 +43,16 @@ input.onButtonPressed(Button.A, () => { }) ``` -## Step 4 +## {Step 4} Press button **A** in the simulator to try the coin toss code. -## Step 5 +## {Step 5} You can animate the coin toss to add the feeling of suspense. Place different ``||basic:show icon||`` blocks before the ``||logic:if||`` to show that the coin is flipping. ```blocks -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function() { basic.showIcon(IconNames.Diamond) basic.showIcon(IconNames.SmallDiamond) basic.showIcon(IconNames.Diamond) @@ -65,10 +65,14 @@ input.onButtonPressed(Button.A, () => { }) ``` -## Step 6 +## {Step 6} If you have a @boardname@, connect it to USB and click ``|Download|`` to transfer your code. -## Step 7 +## {Step 7} Press button **A** for a flip. Test your luck and guess ``heads`` or ``tails`` before the toss is over! + +```template +input.onButtonPressed(Button.A, function() {}) +``` diff --git a/docs/projects/compass.md b/docs/projects/compass.md index 912af2522d2..d3178a1e6b6 100644 --- a/docs/projects/compass.md +++ b/docs/projects/compass.md @@ -1,81 +1,81 @@ # Compass -## Introduction @unplugged +## {Introduction @unplugged} -This tutorial will show you how to program a script that displays which direction the @boardname@ is pointing. Let's get started! +This tutorial shows you how to create a program that displays which direction the @boardname@ is pointing. Let's get started! ![A cartoon of a compass](/static/mb/projects/a5-compass.png) -## Step 1 +## {Step 1} -Store the ``||input:compass heading||`` of the @boardname@ in a variable called ``||variables:degrees||`` in the ``||basic:forever||`` loop. +First, store the ``||input:compass heading||`` of the @boardname@ in a variable called ``||variables:degrees||`` in the ``||basic:forever||`` loop. ```blocks -basic.forever(() => { +basic.forever(function() { let degrees = input.compassHeading() }) ``` -## Step 2 +## {Step 2} ``||logic:If||`` ``||variables:degrees||`` is ``||logic:less than||`` `45`, then the compass heading is mostly pointing toward **North**. ``||basic:Show||`` `N` on the @boardname@. ```blocks -basic.forever(() => { - let degrees = input.compassHeading(); +basic.forever(function() { + let degrees = input.compassHeading() if (degrees < 45) { - basic.showString("N"); + basic.showString("N") } -}); +}) ``` -## Step 3 +## {Step 3} ``||logic:If||`` ``||variables:degrees||`` is less than `135`, the @boardname@ is mostly pointing **East**. ``||basic:Show||`` `E` on the @boardname@. ```blocks -basic.forever(() => { - let degrees = input.compassHeading(); +basic.forever(function() { + let degrees = input.compassHeading() if (degrees < 45) { - basic.showString("N"); + basic.showString("N") } else if (degrees < 135) { - basic.showString("E"); + basic.showString("E") } -}); +}) ``` -## Step 4 +## {Step 4} Go to the simulator and rotate the @boardname@ logo to simulate changes in the compass heading. -## Step 5 +## {Step 5} ``||logic:If||`` ``||variables:degrees||`` is less than `225`, the @boardname@ is mostly pointing **South**. ``||basic:Show||`` `S` on the @boardname@. ```blocks -basic.forever(() => { - let degrees = input.compassHeading(); +basic.forever(function() { + let degrees = input.compassHeading() if (degrees < 45) { - basic.showString("N"); + basic.showString("N") } else if (degrees < 135) { - basic.showString("E"); + basic.showString("E") } else if (degrees < 225) { - basic.showString("S"); + basic.showString("S") } -}); +}) ``` -## Step 6 +## {Step 6} ``||logic:If||`` ``||variables:degrees||`` is less than `315`, the @boardname@ is mostly pointing **West**. ``||basic:Show||`` `W` on the @boardname@. ```blocks -basic.forever(() => { - let degrees = input.compassHeading(); +basic.forever(function() { + let degrees = input.compassHeading() if (degrees < 45) { basic.showString("N"); } @@ -86,16 +86,16 @@ basic.forever(() => { } else if (degrees < 315) { basic.showString("W") } -}); +}) ``` -## Step 7 +## {Step 7} ``||logic:If||`` none of these conditions returned true, then the @boardname@ must be pointing **North** again. Display `N` on the @boardname@. ```blocks -basic.forever(() => { - let degrees = input.compassHeading(); +basic.forever(function() { + let degrees = input.compassHeading() if (degrees < 45) { basic.showString("N"); } @@ -111,12 +111,11 @@ basic.forever(() => { else { basic.showString("N") } -}); +}) ``` -## Step 8 @unplugged +## {Step 9 @unplugged} -If you have a @boardname@, click `|Download|` and follow the screen instructions. -You will have to follow the screen instructions to calibrate your compass. +If you have a @boardname@, click `|Download|` and follow the screen instructions. You will have to follow the screen instructions to calibrate your compass. -https://youtu.be/IL5grHtz_MU \ No newline at end of file +https://youtu.be/IL5grHtz_MU diff --git a/docs/projects/crashy-bird.md b/docs/projects/crashy-bird.md index c995803a9d2..c4325314d31 100644 --- a/docs/projects/crashy-bird.md +++ b/docs/projects/crashy-bird.md @@ -25,10 +25,10 @@ Before creating the code for the game actions, let's first add some controls so ```blocks let bird: game.LedSprite = null -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { bird.change(LedSpriteProperty.Y, -1) }) -input.onButtonPressed(Button.B, () => { +input.onButtonPressed(Button.B, function () { bird.change(LedSpriteProperty.Y, 1) }) ``` @@ -79,11 +79,11 @@ for (let index = 0; index <= 4; index++) { } } -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { bird.change(LedSpriteProperty.Y, -1) }) -input.onButtonPressed(Button.B, () => { +input.onButtonPressed(Button.B, function () { bird.change(LedSpriteProperty.Y, 1) }) ``` @@ -97,7 +97,7 @@ Right click on the ``||value||`` block and rename it to ``||obstacle||`` ```blocks let obstacles: game.LedSprite[] = [] -basic.forever(() => { +basic.forever(function () { for (let obstacle of obstacles) { obstacle.change(LedSpriteProperty.X, -1) } @@ -114,7 +114,7 @@ Make obstacles disappear after reaching leftmost corner. Iterate over all obstac ```blocks let obstacles: game.LedSprite[] = [] -basic.forever(() => { +basic.forever(function () { while (obstacles.length > 0 && obstacles[0].get(LedSpriteProperty.X) == 0) { obstacles.removeAt(0).delete() } @@ -134,7 +134,7 @@ At the moment, our code generates just one vertical obstacle. We need to put obs let emptyObstacleY = 0 let obstacles: game.LedSprite[] = [] -basic.forever(() => { +basic.forever(function () { while (obstacles.length > 0 && obstacles[0].get(LedSpriteProperty.X) == 0) { obstacles.removeAt(0).delete() } @@ -159,7 +159,7 @@ let ticks = 0 let emptyObstacleY = 0 let obstacles: game.LedSprite[] = [] -basic.forever(() => { +basic.forever(function () { while (obstacles.length > 0 && obstacles[0].get(LedSpriteProperty.X) == 0) { obstacles.removeAt(0).delete() } @@ -190,7 +190,7 @@ let ticks = 0 let emptyObstacleY = 0 let obstacles: game.LedSprite[] = [] -basic.forever(() => { +basic.forever(function () { while (obstacles.length > 0 && obstacles[0].get(LedSpriteProperty.X) == 0) { obstacles.removeAt(0).delete() } @@ -226,17 +226,17 @@ let emptyObstacleY = 0 let obstacles: game.LedSprite[] = [] let index = 0 let bird: game.LedSprite = null -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { bird.change(LedSpriteProperty.Y, -1) }) -input.onButtonPressed(Button.B, () => { +input.onButtonPressed(Button.B, function () { bird.change(LedSpriteProperty.Y, 1) }) index = 0 obstacles = [] bird = game.createSprite(0, 2) bird.set(LedSpriteProperty.Blink, 300) -basic.forever(() => { +basic.forever(function () { while (obstacles.length > 0 && obstacles[0].get(LedSpriteProperty.X) == 0) { obstacles.removeAt(0).delete() } diff --git a/docs/projects/dice.md b/docs/projects/dice.md index 6b571a2f8e5..9be9102ab5a 100644 --- a/docs/projects/dice.md +++ b/docs/projects/dice.md @@ -1,58 +1,63 @@ # Dice -## Introduction @unplugged +## {Introduction @unplugged} -Let's turn the @boardname@ into a dice! -(Want to learn how the accelerometer works? [Watch this video](https://youtu.be/byngcwjO51U)). +Let's create some digital 🎲 dice 🎲 with our micro:bit! ![A microbit dice](/static/mb/projects/dice.png) -## Step 1 +## {Step 1} -We need 3 pieces of code: one to detect a throw (shake), another to pick a random number, and then one to show the number. - -Place the ``||input:on shake||`` block onto the editor workspace. It runs code when you shake the @boardname@. +The ``||input:on shake||`` block runs code when you shake 👋 the @boardname@. From the ``||basic:Basic||`` category, get a ``||basic:show number||`` block and place it inside the ``||input:on shake||`` block to display a number. ```blocks -input.onGesture(Gesture.Shake, () => { - +input.onGesture(Gesture.Shake, function() { + //@highlight + basic.showNumber(0) }) ``` -## Step 2 +## {Step 2} -Get a ``||basic:show number||`` block and place it inside the ``||input:on shake||`` block to display a number. +Press the white **SHAKE** button on the micro:bit on-screen simulator, or move your cursor quickly back and forth over the simulator. Do you see the number 0 appear? ⭐ Great job! ⭐ -```blocks -input.onGesture(Gesture.Shake, () => { - basic.showNumber(0) -}) -``` +## {Step 3} -## Step 3 - -Put a ``||Math:pick random||`` block in the ``||basic:show number||`` block to pick a random number. +But we don't want to show 0 on our dice all the time. From the ``||math:Math||`` Toolbox category, drag a ``||Math:pick random||`` block and drop it into the ``||basic:show number||`` block replacing the **0**. ```blocks -input.onGesture(Gesture.Shake, () => { +input.onGesture(Gesture.Shake, function() { + //@highlight basic.showNumber(randint(0, 10)) }) ``` -## Step 4 +## {Step 4} -A typical dice shows values from `1` to `6`. So, in ``||Math:pick random||``, don't forget to choose the right minimum and maximum values! +A typical dice shows values from 1 to 6 dots. So, in the ``||Math:pick random||`` block, change the minimum value to **1** and the maximum value to **6**. ```blocks -input.onGesture(Gesture.Shake, () => { +input.onGesture(Gesture.Shake, function() { + //@highlight basic.showNumber(randint(1, 6)) }) ``` -## Step 5 +## {Step 5} + +Press the white **SHAKE** button again on the micro:bit simulator. Do you see random numbers between 1 and 6 appear? ⭐ Great job! ⭐ + +## {Step 6} -Use the simulator to try out your code. Does it show the number you expected? +If you have a @boardname@ device, connect it to your computer and click the ``|Download|`` button. Follow the instructions to transfer your code onto the @boardname@. Once your code has been downloaded, attach your micro:bit to a battery pack and use it as digital 🎲 dice for your next boardgame! -## Step 6 +## {Step 7} +Go further - Try adding some Music blocks to make a sound when you shake your dice, or use the micro:bit LED lights to show number values. Want to learn how the micro:bit motion detector or accelerometer works? [Watch this video](https://youtu.be/byngcwjO51U). -If you have a @boardname@ connected, click ``|Download|`` and transfer your code to the @boardname@! +```validation.global +# BlocksExistValidator +``` + +```template +input.onGesture(Gesture.Shake, function() {}) +``` diff --git a/docs/projects/duct-tape-watch.md b/docs/projects/duct-tape-watch.md index 8e8db57352f..4181e0f14d4 100644 --- a/docs/projects/duct-tape-watch.md +++ b/docs/projects/duct-tape-watch.md @@ -8,10 +8,6 @@ * Roll of duct tape (maybe 2 rolls if you want another color) * Velcro -## Flipgrid - -https://flipgrid.com/30f0429c - ## Steps ### Step 1 - Cut the pieces of tape diff --git a/docs/projects/electric-guitar.md b/docs/projects/electric-guitar.md new file mode 100644 index 00000000000..91742308409 --- /dev/null +++ b/docs/projects/electric-guitar.md @@ -0,0 +1,27 @@ +# Electric Guitar + +## ~avatar avatar + +Make an electric guitar that you can play real chords with using the micro:bit. + +## ~ + +https://youtu.be/Yocsl_80YsY + +## Materials + +* micro:bit and optional battery pack +* 4 crocodile clip leads +* cardboard, scissors, glue, tin foil +* headphones, buzzer, or powered speaker + +## Activities + +* [Make](/projects/electric-guitar/make) +* [Code](/projects/electric-guitar/code) + +## ~button /projects/electric-guitar/make + +Let's get started! + +## ~ diff --git a/docs/projects/electric-guitar/code.md b/docs/projects/electric-guitar/code.md new file mode 100644 index 00000000000..f87e0c88f20 --- /dev/null +++ b/docs/projects/electric-guitar/code.md @@ -0,0 +1,42 @@ +# Code + +Let's add code so that whenever we press or touch the foil chords it will produce sound. + +From the [Make](/projects/electric-guitar/make.md) project, we know that whenever user touches the chords, sound will be produced and diffrent chords will produce diffrent sounds. + +## Code your electric guitar + +Download this code to your micro:bit. It creates the tones that play when you press the foil strips on the guitar. + +```blocks +input.onButtonPressed(Button.A, function () { + F = F / 2 + A = A / 2 + C = C / 2 + E = E / 2 +}) +input.onPinPressed(TouchPin.P2, function () { + music.playTone(988, music.beat(BeatFraction.Whole)) + music.playTone(165, music.beat(BeatFraction.Whole)) + music.playTone(932, music.beat(BeatFraction.Whole)) +}) +input.onButtonPressed(Button.B, function () { + F = F * 2 + A = A * 2 + C = C * 2 + E = E * 2 +}) +input.onPinPressed(TouchPin.P1, function () { + music.playTone(F, music.beat(BeatFraction.Half)) + music.playTone(A, music.beat(BeatFraction.Half)) + music.playTone(C, music.beat(BeatFraction.Half)) +}) +let E = 0 +let C = 0 +let A = 0 +let F = 0 +F = 349 +A = 440 +C = 523 +E = 659 +``` diff --git a/docs/projects/electric-guitar/make.md b/docs/projects/electric-guitar/make.md new file mode 100644 index 00000000000..11ca30c93c9 --- /dev/null +++ b/docs/projects/electric-guitar/make.md @@ -0,0 +1,40 @@ +# Make + +A guitar is a plucked stringed musical instrument. Normally, the guitar is an expensive instrument, but here we create a cheap and convenient DIY instrument that can help you enjoy and play real chords on an electric micro:bit guitar. Have fun and enjoy playing the micro:bit guitar by shifting the pitch up and down octaves. + +## How it Works? + +When you touch pin **1** or pin **2** and **GND** it will play a broken chord, but now you can move the chord down an octave (lowering its pitch) by pressing button **A** and move it up an octave (raising its pitch) by pressing button **B**. + +The pitch (frequency) of a note doubles when you move up one octave: `middle A` has a frequency of 440Hz (440 vibrations per second), `high A` has a frequency of 880Hz. This is why making the vibrating part of guitar strings different lengths with your fingers changes the pitch of the note being played. + +## Materials you need + +* micro:bit and optional battery pack +* 4 crocodile clip leads +* cardboard, scissors, glue, tin foil +* headphones, buzzer, or powered speaker + +## How to build and play your electric guitar + +Watch this video to see how to make your electric guitar and play it: + +https://youtu.be/Yocsl_80YsY + +You can connect the electric guitar to some headphones with some crocodile clips you attach to the phone jack. + +![Output connections for sound](/static/mb/projects/electric-guitar/connections.jpg) + +Here are two pictures of the finished electric guitar with its connections: + +![Electric guitar project 1](/static/mb/projects/electric-guitar/guitar-board1.jpg) + +![Electric guitar project 2](/static/mb/projects/electric-guitar/guitar-board2.jpg) + +Let's go on to code the guitar! + +### ~button /projects/electric-guitar/code + +Code + +### ~ diff --git a/docs/projects/fireflies.md b/docs/projects/fireflies.md index 7c680d3513d..33bce093a6a 100644 --- a/docs/projects/fireflies.md +++ b/docs/projects/fireflies.md @@ -34,7 +34,7 @@ When the clock reaches "noon" (let's pick `8` as noon), we turn on the screen br ```block // the clock ticker let clock = 0 -basic.forever(() => { +basic.forever(function () { // if clock "hits noon", flash the screen if (clock >= 8) { // flash @@ -61,7 +61,7 @@ When a firefly flashes, it also sends a number over radio using ``||radio:radio ```block // the clock ticker let clock = 0 -basic.forever(() => { +basic.forever(function () { // if clock "hits noon", flash the screen if (clock >= 8) { // notify neighbors @@ -111,7 +111,7 @@ radio.onReceivedNumber(function (receivedNumber) { // advance clock to catch up neighbors clock += 1 }) -basic.forever(() => { +basic.forever(function () { // if clock hits noon, flash the screen if (clock >= 8) { // notify neighbors diff --git a/docs/projects/flashing-heart.md b/docs/projects/flashing-heart.md index 4c220f5361f..4c4c39dc104 100644 --- a/docs/projects/flashing-heart.md +++ b/docs/projects/flashing-heart.md @@ -1,22 +1,22 @@ # Flashing Heart -## Introduction @unplugged - -Learn how to use the LEDs and make a flashing heart! -(Want to learn how lights work? [Watch this video](https://youtu.be/qqBmvHD5bCw)). +## Code a Flashing Heart @unplugged +Code the lights on the micro:bit into a flashing heart animation! 💖 ![Heart shape in the LEDs](/static/mb/projects/flashing-heart/sim.gif) -## Step 1 @fullscreen +## {Step 1 @fullscreen} -Place the ``||basic:show leds||`` block in the ``||basic:forever||`` block and draw a heart. +Click on the ``||basic:Basic||`` category in the Toolbox. +Drag the ``||basic:show leds||`` block into the ``||basic:forever||`` block. +Then in the ``||basic:show leds||`` block, click on the squares to draw a heart design. ![An animation that shows how to drag a block and paint a heart](/static/mb/projects/flashing-heart/showleds.gif) -## Step 2 +## {Step 2} -Place another ``||basic:show leds||`` block. You can leave it blank and draw what you want. +Drag another ``||basic:show leds||`` block underneath the first. ```blocks basic.forever(function() { @@ -35,10 +35,18 @@ basic.forever(function() { }) ``` -## Step 3 +## {Step 3} + +Look at the @boardname@ on the screen. Do you see a flashing heart animation? ⭐ Great job! ⭐ + +## {Step 4} + +If you have a @boardname@ device, connect it to your computer and click the ``|Download|`` button. Follow the instructions to transfer your code onto the @boardname@ and watch the hearts flash! -Look at the virtual @boardname@, you should see the heart and your drawing blink on the screen. +## {Step 5} -## Step 4 +Go further - try adding more ``||basic:show leds||`` blocks to create a longer animation! Learn more about how the @boardname@ lights work by watching [this video](https://youtu.be/qqBmvHD5bCw). -If you have a @boardname@ connected, click ``|Download|`` to transfer your code and watch the hearts flash! +```template +basic.forever(function() {}) +``` \ No newline at end of file diff --git a/docs/projects/games.md b/docs/projects/games.md index f27a6b08b51..1d9dc539411 100644 --- a/docs/projects/games.md +++ b/docs/projects/games.md @@ -24,6 +24,12 @@ Fun games to build with your @boardname@. "cardType": "tutorial" }] }, { + "name": "Rock Paper Scissors V2", + "url":"/projects/rock-paper-scissors-v2", + "description": "Rock Paper Scissors with Sounds for micro:bit V2!", + "imageUrl":"/static/mb/projects/a4-motion-v2.png", + "cardType": "tutorial" +},{ "name": "Coin Flipper", "url":"/projects/coin-flipper", "description": "Guess the coin toss and see if you're lucky.", @@ -77,6 +83,7 @@ Fun games to build with your @boardname@. "description": "Try to guess words with your friends!", "imageUrl": "/static/mb/projects/heads-guess.png", "cardType": "tutorial", + "youTubeId": "WgMj1AT2G38", "otherActions": [{ "url": "/projects/spy/heads-guess", "editor": "py", @@ -97,6 +104,7 @@ Fun games to build with your @boardname@. "description": "Button smashing rope pulling games using LEDs", "imageUrl":"/static/mb/projects/tug-of-led.png", "cardType": "tutorial", + "youTubeId": "oZrvVB4cGWU", "otherActions": [{ "url": "/projects/spy/tug-of-led", "editor": "py", @@ -117,6 +125,7 @@ Fun games to build with your @boardname@. "description": "Use the game blocks to create a skill game", "imageUrl": "/static/mb/projects/snap-the-dot.png", "cardType": "tutorial", + "youTubeId": "ew15T97VrF4", "otherActions": [{ "url": "/projects/spy/snap-the-dot", "editor": "py", @@ -142,8 +151,4 @@ Fun games to build with your @boardname@. "url":"/projects/crashy-bird", "imageUrl":"/static/mb/projects/crashy-bird.png" }] -``` - -## Flipgrid - -https://flipgrid.com/makecodemicrobit +``` \ No newline at end of file diff --git a/docs/projects/guitar.md b/docs/projects/guitar.md index 3ea184b7726..b6953aad5ac 100644 --- a/docs/projects/guitar.md +++ b/docs/projects/guitar.md @@ -37,8 +37,4 @@ https://youtu.be/GYmdTFvxz80 Let's get started! -## ~ - -## Flipgrid - -https://flipgrid.com/e302fe4b +## ~ \ No newline at end of file diff --git a/docs/projects/guitar/accelerometer.md b/docs/projects/guitar/accelerometer.md index 3062aa618f2..13472ef320f 100644 --- a/docs/projects/guitar/accelerometer.md +++ b/docs/projects/guitar/accelerometer.md @@ -53,7 +53,7 @@ acceleration of gravity. ## Step 1: Graphing acceleration ```blocks -basic.forever(() => { +basic.forever(function () { led.plotBarGraph(input.acceleration(Dimension.Y), 1023) }) ``` @@ -76,11 +76,11 @@ Try graphing the acceleration along the **X** and **Z** axis. Can you explain th ## Step 2: Mapping acceleration to Beat **@boardname@ sensors produce signal values between 0 to 1023. The *[map block](/reference/pins/map)* converts the signal to a desired range.** ```blocks -basic.forever(() => { +basic.forever(function () { music.setTempo(pins.map(Math.abs(input.acceleration(Dimension.Y)), 0, 1023, 60, 320)) - music.playTone(Note.C, music.beat(BeatFraction.Quarter)); + music.playTone(Note.C, music.beat(BeatFraction.Quarter)) }) ``` @@ -94,14 +94,14 @@ basic.forever(() => { **Put it all together!** ```blocks -basic.forever(() => { +basic.forever(function () { music.setTempo(pins.map(Math.abs(input.acceleration(Dimension.Y)), 0, 1023, 60, 320)) music.playTone( input.lightLevel() * 25, music.beat(BeatFraction.Quarter) - ); + ) }) ``` **Combine the code above with the light sensor tone control code from the previous activity** diff --git a/docs/projects/guitar/displaybuttons.md b/docs/projects/guitar/displaybuttons.md index 9f5ea203356..806e78caa90 100644 --- a/docs/projects/guitar/displaybuttons.md +++ b/docs/projects/guitar/displaybuttons.md @@ -37,7 +37,7 @@ Headphones . # . # . . # # # . `); -input.onButtonPressed(Button.A, () => {}); +input.onButtonPressed(Button.A, function () {}) music.playTone(Note.C, music.beat(BeatFraction.Quarter)) music.rest(music.beat(BeatFraction.Whole)) music.beat(BeatFraction.Quarter) @@ -52,7 +52,7 @@ Open @homeurl@ in your web browser . # # # . . # . # . . # # # . - `); + `) ``` From **Basics**, drag a **show LEDs** block into the coding area * Create a face with LEDs @@ -63,7 +63,7 @@ Follow the instructions to move the code to your @boardname@. ## Step 2: Add Smiley LED Button Events ```blocks -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { basic.showLeds(` . # . # . . . . . . @@ -72,7 +72,7 @@ input.onButtonPressed(Button.A, () => { . # # # . `) }) -input.onButtonPressed(Button.B, () => { +input.onButtonPressed(Button.B, function () { basic.showLeds(` . # . # . . . . . . @@ -115,7 +115,7 @@ Connect the headphones with crocodile clips The **play tone** block allows a range letter note tones from **C** to **B5**. Songs are played using sequences notes. Like the beginning of a birthday song (C, C, D, C, F, E). ```blocks -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { music.playTone(Note.C, music.beat(BeatFraction.Quarter)) music.rest(music.beat(BeatFraction.Whole)) music.playTone(Note.C, music.beat(BeatFraction.Quarter)) @@ -133,7 +133,7 @@ input.onButtonPressed(Button.A, () => { ## ~ ## Step 4: Add Tone Playing Events for Buttons A & B ```blocks -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { basic.showLeds(` . # . # . . . . . . @@ -143,7 +143,7 @@ input.onButtonPressed(Button.A, () => { `) music.playTone(Note.A, music.beat(BeatFraction.Whole)) }) -input.onButtonPressed(Button.B, () => { +input.onButtonPressed(Button.B, function () { basic.showLeds(` . # . # . . . . . . diff --git a/docs/projects/guitar/lightsensor.md b/docs/projects/guitar/lightsensor.md index 4a54f8d7188..ecce505885f 100644 --- a/docs/projects/guitar/lightsensor.md +++ b/docs/projects/guitar/lightsensor.md @@ -33,7 +33,7 @@ The forever loop really does run forever. The forever loop is useful when there ## Blocks ```cards -basic.forever(() => {}) +basic.forever(function () {}) input.lightLevel() led.plotBarGraph(0, 255) music.playTone(Note.C, music.beat(BeatFraction.Quarter)) @@ -41,7 +41,7 @@ music.playTone(Note.C, music.beat(BeatFraction.Quarter)) ## Step 1: Create a light level detector ```blocks -basic.forever(() => { +basic.forever(function () { led.plotBarGraph(input.lightLevel(), 255) }) ``` @@ -80,7 +80,7 @@ music.playTone(261, music.beat(BeatFraction.Half)) ## Step 3: Multiply Frequency using Math blocks ```blocks -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { music.playTone(261 * 2, music.beat(BeatFraction.Half)) }) ``` @@ -95,7 +95,7 @@ Create a **play tone** block using a **Math** section, **multiplication** block ## Step 4: Control the Frequency with the light input ```blocks -basic.forever(() => { +basic.forever(function () { music.playTone(input.lightLevel() * 25, music.beat(BeatFraction.Quarter)) }) ``` diff --git a/docs/projects/guitar/pinpress.md b/docs/projects/guitar/pinpress.md index a290a7e7388..bcf9fe37f88 100644 --- a/docs/projects/guitar/pinpress.md +++ b/docs/projects/guitar/pinpress.md @@ -22,9 +22,9 @@ Use pin press to switch guitar play on/off ```cards let on = false -on; +on if (on) { } else {} -input.onPinPressed(TouchPin.P1, () => {}) +input.onPinPressed(TouchPin.P1, function () {}) ``` @@ -43,13 +43,13 @@ input.onPinPressed(TouchPin.P1, () => {}) ## Step 1: Pin Press Test ```blocks -input.onPinPressed(TouchPin.P0, () => { +input.onPinPressed(TouchPin.P0, function () { basic.showNumber(0) }) -input.onPinPressed(TouchPin.P1, () => { +input.onPinPressed(TouchPin.P1, function () { basic.showNumber(1) }) -input.onPinPressed(TouchPin.P2, () => { +input.onPinPressed(TouchPin.P2, function () { basic.showNumber(2) }) ``` @@ -88,14 +88,14 @@ https://youtu.be/YkymZGNmkrE **between ON and OFF** ```blocks let on = false -basic.forever(() => { +basic.forever(function () { if (on == true) { basic.showString("ON") } else { basic.showString("OFF") } }) -input.onPinPressed(TouchPin.P1, () => { +input.onPinPressed(TouchPin.P1, function () { if (on == true) { on = false } else { @@ -110,10 +110,10 @@ input.onPinPressed(TouchPin.P1, () => { **Test by touching `P1` to toggle the LED message between ON and OFF** *Final code* -TODO: do we want to use `on = !on;` or be more direct in flipping the switch? `on = true; on = false;` +TODO: do we want to use `on = !on` or be more direct in flipping the switch? `on = true` or `on = false` ```blocks let on = false -basic.forever(() => { +basic.forever(function () { if (on) { music.setTempo(pins.map(Math.abs(input.acceleration(Dimension.Y)), 0, 1023, @@ -121,13 +121,13 @@ basic.forever(() => { music.playTone( input.lightLevel() * 25, music.beat(BeatFraction.Quarter) - ); + ) } else { music.rest(music.beat()) } }) -input.onPinPressed(TouchPin.P1, () => { - on = !on; +input.onPinPressed(TouchPin.P1, function () { + on = !on }) ``` ## Now Play! diff --git a/docs/projects/hack-your-headphones/code.md b/docs/projects/hack-your-headphones/code.md index b78d6b1e26b..e333653e965 100644 --- a/docs/projects/hack-your-headphones/code.md +++ b/docs/projects/hack-your-headphones/code.md @@ -6,20 +6,20 @@ Have you ever tried to making beat box sounds based on the light level? Let's tr ## ~ -Let's start by adding a variable where you can store data. Name the variable as ``light`` and ``||variables:set||`` the value of the variable to the ``||input:light level||`` block from the ``||input:Input||`` drawer. This will get the light level as some value between `0` (dark) and `255` (bright). The light is measured by using various LEDs from the screen. Your code will look like this: - +Let's start by playing music when the **A** button is pressed. To do that, register an event handler that will execute whenever you click on the **A** button. Open the ``||input:Input||`` drawer and get out an ``||input:on button A pressed||`` block. Next, add a ``||music:rest||`` to play nothing for `1/16` of a beat. ```blocks -let light = input.lightLevel(); +input.onButtonPressed(Button.A, function () { + music.rest(music.beat(BeatFraction.Sixteenth)) +}); ``` -We also want to play music when the **A** button is pressed. To do that, register an event handler that will execute whenever you click on the **A** button. Open the ``||input:Input||`` drawer and get out an ``||input:on button A pressed||`` block. Next, add a ``||music:rest||`` to play nothing for `1/16` of a beat. Pull the ``||variables:set light||`` block in there too. Your code should look like this: - +We also want to add a variable where you can store data. Name the variable ``light`` and ``||variables:set||`` the value of the variable to the ``||input:light level||`` block from the ``||input:Input||`` drawer. This will get the light level as some value between `0` (dark) and `255` (bright). The light is measured by using various LEDs from the screen. Your code will look like this: ```blocks -input.onButtonPressed(Button.A, () => { - music.rest(music.beat(BeatFraction.Sixteenth)); - let light = input.lightLevel(); +input.onButtonPressed(Button.A, function () { + music.rest(music.beat(BeatFraction.Sixteenth)) + let light = input.lightLevel() }); ``` @@ -29,14 +29,14 @@ Click on the ``||logic:Logic||`` drawer and find an ``||logic:if||`` block to us * If this condition is not `true`, play ``||music:ring tone||`` for ``Middle A`` ```blocks -input.onButtonPressed(Button.A, () => { - music.rest(music.beat(BeatFraction.Sixteenth)); - let light = input.lightLevel(); +input.onButtonPressed(Button.A, function () { + music.rest(music.beat(BeatFraction.Sixteenth)) + let light = input.lightLevel() if (light < 25) { - music.ringTone(music.noteFrequency(Note.C)); + music.ringTone(music.noteFrequency(Note.C)) } else { - music.ringTone(music.noteFrequency(Note.A)); + music.ringTone(music.noteFrequency(Note.A)) } }); ``` @@ -50,26 +50,26 @@ Now, we want to add more conditional statements by clicking on the **(+)** at th * If these conditions are not true, play ``||music:ring tone||`` ``Middle A`` ```blocks -input.onButtonPressed(Button.A, () => { - music.rest(music.beat(BeatFraction.Sixteenth)); - let light = input.lightLevel(); +input.onButtonPressed(Button.A, function () { + music.rest(music.beat(BeatFraction.Sixteenth)) + let light = input.lightLevel() if (light < 25) { - music.ringTone(music.noteFrequency(Note.C)); + music.ringTone(music.noteFrequency(Note.C)) } else if (light < 50) { - music.ringTone(music.noteFrequency(Note.D)); + music.ringTone(music.noteFrequency(Note.D)) } else if (light < 100) { - music.ringTone(music.noteFrequency(Note.E)); + music.ringTone(music.noteFrequency(Note.E)) } else if (light < 150) { - music.ringTone(music.noteFrequency(Note.F)); + music.ringTone(music.noteFrequency(Note.F)) } else if (light < 180) { - music.ringTone(music.noteFrequency(Note.G)); + music.ringTone(music.noteFrequency(Note.G)) } else { - music.ringTone(music.noteFrequency(Note.A)); + music.ringTone(music.noteFrequency(Note.A)) } }); ``` diff --git a/docs/projects/heads-guess.md b/docs/projects/heads-guess.md index 0c83c2a791f..28ca891d986 100644 --- a/docs/projects/heads-guess.md +++ b/docs/projects/heads-guess.md @@ -1,11 +1,11 @@ # Heads Guess! -## Introduction @unplugged +## {Introduction @unplugged} This is a simple remake of the famous **Heads Up!** game. The player holds the @boardname@ on the forehead and has 30 seconds to guess words displayed on the screen. If the guess is correct, the player tilts the @boardname@ forward; to pass, the player tilts it backwards. -## Step 1 +## {Step 1} Put in code to ``||game:start a countdown||`` of 30 seconds. @@ -13,17 +13,17 @@ Put in code to ``||game:start a countdown||`` of 30 seconds. game.startCountdown(30000) ``` -## Step 2 +## {Step 2} -Create a ``||arrays:text list||`` of words to guess. You will find **Arrays** under **Advanced**. +Create a new array of words to guess and name it ``||arrays:wordList||``. You will find **Arrays** under **Advanced**. ```blocks -let text_list: string[] = [] -text_list = ["PUPPY", "CLOCK", "NIGHT"] +let wordList: string[] = [] +wordList = ["PUPPY", "CLOCK", "NIGHT"] game.startCountdown(30000) ``` -## Step 3 +## {Step 3} Add an event to run code when the @boardname@ ``||input:logo||`` is pointing ``||input:up||``. This is the gesture to get a new word. @@ -33,35 +33,35 @@ input.onGesture(Gesture.LogoUp, function () { }) ``` -## Step 4 +## {Step 4} -The items in ``||arrays:text list||`` are numbered ``0`` to ``length - 1``. +The items in ``||arrays:wordList||`` are numbered ``0`` to ``length - 1``. Add code to pick a ``||math:random||`` ``||variables:index||``. ```blocks -let text_list: string[] = [] +let wordList: string[] = [] let index = 0 input.onGesture(Gesture.LogoUp, function () { // @highlight - index = randint(0, text_list.length - 1) + index = randint(0, wordList.length - 1) }) ``` -## Step 5 +## {Step 5} -Add code to ``||basic:show||`` the value of the item stored at ``||variables:index||`` in ``||arrays:text list||``. +Add code to ``||basic:show||`` the value of the item stored at ``||variables:index||`` in ``||arrays:wordList||``. ```blocks -let text_list: string[] = [] +let wordList: string[] = [] let index = 0 input.onGesture(Gesture.LogoUp, function () { - index = randint(0, text_list.length - 1) + index = randint(0, wordList.length - 1) // @highlight - basic.showString(text_list[index]) + basic.showString(wordList[index]) }) ``` -## Step 6 +## {Step 6} Use an event to run code when the @boardname@ ``||input:screen||`` is pointing ``||input:down||``. This is the gesture for a correct guess. @@ -71,7 +71,7 @@ input.onGesture(Gesture.ScreenDown, function () { }) ``` -## Step 7 +## {Step 7} Put in code to add points to the ``||game:score||``. @@ -82,9 +82,9 @@ input.onGesture(Gesture.ScreenDown, function () { }) ``` -## Step 8 +## {Step 8} -Add anonther event to run code when the @boardname@ ``||input:screen||`` is pointing ``||input:up||``. +Add another event to run code when the @boardname@ ``||input:screen||`` is pointing ``||input:up||``. This is the gesture for a pass. ```blocks @@ -92,7 +92,7 @@ input.onGesture(Gesture.ScreenUp, function () { }) ``` -## Step 9 +## {Step 9} For the pass gesture, add code to remove a ``||game:life||`` from the player. diff --git a/docs/projects/hot-or-cold.md b/docs/projects/hot-or-cold.md index ffc8638d925..ee5cd79c74b 100644 --- a/docs/projects/hot-or-cold.md +++ b/docs/projects/hot-or-cold.md @@ -32,7 +32,7 @@ radio.setTransmitPower(6) The beacon just needs to send a radio message every now and then. So, to pace the transmits and give some visual feedback, we add some ``||basic:show icon||`` blocks to animate the screen. ```blocks -basic.forever(() => { +basic.forever(function () { radio.sendNumber(0) basic.showIcon(IconNames.Heart) basic.showIcon(IconNames.SmallHeart) @@ -142,7 +142,7 @@ radio.onReceivedNumber(function (receivedNumber) { To see the current score, we add an ``||input:on button pressed||`` that displays the score on the screen when the **A** button is pressed. ```block -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { basic.showNumber(game.score()) }) ``` @@ -172,7 +172,7 @@ radio.onReceivedNumber(function (receivedNumber) { } } }) -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { basic.showNumber(game.score()) }) radio.setGroup(1) diff --git a/docs/projects/hot-or-cold/beacon.md b/docs/projects/hot-or-cold/beacon.md index 4a02e68b71a..21c844526bd 100644 --- a/docs/projects/hot-or-cold/beacon.md +++ b/docs/projects/hot-or-cold/beacon.md @@ -21,7 +21,7 @@ radio.setTransmitPower(6) The beacon just needs to send a radio message every now and then. So, to pace the transmits and give some visual feedback, we add some ``||basic:show icon||`` blocks to animate the screen. ```blocks -basic.forever(() => { +basic.forever(function () { radio.sendNumber(0) basic.showIcon(IconNames.Heart) basic.showIcon(IconNames.SmallHeart) diff --git a/docs/projects/hot-or-cold/multi-beacons.md b/docs/projects/hot-or-cold/multi-beacons.md index 6ce5ab8f7b4..5f5fdf45830 100644 --- a/docs/projects/hot-or-cold/multi-beacons.md +++ b/docs/projects/hot-or-cold/multi-beacons.md @@ -47,7 +47,7 @@ radio.onReceivedNumber(function (receivedNumber) { To see the current score, we add an ``||input:on button pressed||`` that displays the score on the screen when the **A** button is pressed. ```block -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { basic.showNumber(game.score()) }) ``` @@ -77,7 +77,7 @@ radio.onReceivedNumber(function (receivedNumber) { } } }) -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { basic.showNumber(game.score()) }) radio.setGroup(1) diff --git a/docs/projects/hot-or-cold/seekers.md b/docs/projects/hot-or-cold/seekers.md index ee45d338328..493689404f6 100644 --- a/docs/projects/hot-or-cold/seekers.md +++ b/docs/projects/hot-or-cold/seekers.md @@ -22,7 +22,7 @@ To determine how far away or how close they are, we use the signal strength of e let signal = 0; radio.onReceivedNumber(function (receivedNumber) { signal = radio.receivedPacket(RadioPacketProperty.SignalStrength) - basic.showNumber(signal); + basic.showNumber(signal) }); radio.setGroup(1) ``` @@ -47,7 +47,7 @@ Here is an example that uses ``-95`` or less for cold, between ``-95`` and ``-80 ### ~ ```blocks -let signal = 0; +let signal = 0 radio.onReceivedNumber(function (receivedNumber) { signal = radio.receivedPacket(RadioPacketProperty.SignalStrength) if (signal < -90) { diff --git a/docs/projects/hot-potato.md b/docs/projects/hot-potato.md index a17a19c37e4..5321d7d62e5 100644 --- a/docs/projects/hot-potato.md +++ b/docs/projects/hot-potato.md @@ -1,11 +1,11 @@ # Hot Potato -## Introduction @unplugged +## {Introduction @unplugged} In this game, you will start a timer with a random countdown of a number of seconds. When the timer is off, the game is over and whoever is holding the potato has lost! Watch the tutorial on the [MakeCode YouTube channel](https://youtu.be/xLEy1B_gWKY). -## Step 1 +## {Step 1} Add an event to run code when ``||input:button A is pressed||``. @@ -14,7 +14,7 @@ input.onButtonPressed(Button.A, function () { }) ``` -## Step 2 +## {Step 2} Make a ``||variables:timer||`` variable and ``||variables:set||`` it to a ``||math:random value||`` between ``5`` and ``15``. @@ -29,7 +29,7 @@ input.onButtonPressed(Button.A, function () { }) ``` -## Step 3 +## {Step 3} Add code to ``||basic:show||`` that the game started. @@ -42,7 +42,7 @@ input.onButtonPressed(Button.A, function () { }) ``` -## Step 4 +## {Step 4} Put in a loop to repeat code ``||loops:while||`` ``||variables:timer||`` ``||logic:is positive||``. When `timer` is negative, the game is over. @@ -58,9 +58,9 @@ input.onButtonPressed(Button.A, function () { }) ``` -## Step 5 +## {Step 5} -Inside the ``||loops:while||`` loop, add code to ``||variables:decrease||`` the timer ``||basic:every second||``. +Inside the ``||loops:while||`` loop, add code to ``||variables:decrease||`` the timer for every ``||basic:pause||`` of one second. ```blocks let timer = 0 @@ -76,7 +76,7 @@ input.onButtonPressed(Button.A, function () { }) ``` -## Step 5 +## {Step 6} **After** the ``||loops:while||`` loop is done, add code to ``||basic:show||`` that the game is over. @@ -94,6 +94,10 @@ input.onButtonPressed(Button.A, function () { }) ``` -## Step 6 +## {Step 7} `|Download|` your code to your @boardname@, tape it to a potato and play the game with your friends! + +```template +// +``` \ No newline at end of file diff --git a/docs/projects/inchworm.md b/docs/projects/inchworm.md index 7c8d554bf61..55444b96a61 100644 --- a/docs/projects/inchworm.md +++ b/docs/projects/inchworm.md @@ -40,8 +40,4 @@ https://youtu.be/BiZLjugXMbM Let's get started! -## ~ - -## Flipgrid - -https://flipgrid.com/1b675b12 \ No newline at end of file +## ~ \ No newline at end of file diff --git a/docs/projects/inchworm/code.md b/docs/projects/inchworm/code.md index 750dab1ed84..d12e624e358 100644 --- a/docs/projects/inchworm/code.md +++ b/docs/projects/inchworm/code.md @@ -15,11 +15,11 @@ Add code to make the inchworm move. In order for the inchworm to move, the @boardname@ needs to command the servo to move between ``0`` and ``180`` degrees at a certain pace. The code below starts the inchworm moving when the **A** button is pressed. ```blocks -input.onButtonPressed(Button.A, () => { - pins.servoWritePin(AnalogPin.P0, 0); - basic.pause(500); - pins.servoWritePin(AnalogPin.P0, 180); - basic.pause(500); +input.onButtonPressed(Button.A, function () { + pins.servoWritePin(AnalogPin.P0, 0) + basic.pause(500) + pins.servoWritePin(AnalogPin.P0, 180) + basic.pause(500) }); ``` diff --git a/docs/projects/inchworm/connect.md b/docs/projects/inchworm/connect.md index feb009701be..e5510b3dc91 100644 --- a/docs/projects/inchworm/connect.md +++ b/docs/projects/inchworm/connect.md @@ -17,7 +17,7 @@ radio.onReceivedNumber(function (receivedNumber: number) { pins.servoWritePin(AnalogPin.P0, 180) basic.pause(500) }) -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { radio.sendNumber(0) }) ``` diff --git a/docs/projects/infection.md b/docs/projects/infection.md index eac48628ab4..bacca4949ee 100644 --- a/docs/projects/infection.md +++ b/docs/projects/infection.md @@ -106,10 +106,10 @@ As a result, it will not convert back to blocks. * Healthy: IconNames.Happy * */ -const INCUBATION = 20000; // time before showing symptoms -const DEATH = 40000; // time before dying off the disease -const RSSI = -45; // db -const TRANSMISSIONPROB = 40; // % probability to transfer disease +const INCUBATION = 20000 // time before showing symptoms +const DEATH = 40000 // time before dying off the disease +const RSSI = -45 // db +const TRANSMISSIONPROB = 40 // % probability to transfer disease enum GameState { Stopped, @@ -147,366 +147,366 @@ const GameIcons = { class Message { - private _data: Buffer; + private _data: Buffer constructor(input?: Buffer) { - this._data = input || control.createBuffer(13); + this._data = input || control.createBuffer(13) } get kind(): number { - return this._data.getNumber(NumberFormat.Int8LE, 0); + return this._data.getNumber(NumberFormat.Int8LE, 0) } set kind(x: number) { - this._data.setNumber(NumberFormat.Int8LE, 0, x); + this._data.setNumber(NumberFormat.Int8LE, 0, x) } get fromSerialNumber(): number { - return this._data.getNumber(NumberFormat.Int32LE, 1); + return this._data.getNumber(NumberFormat.Int32LE, 1) } set fromSerialNumber(x: number) { - this._data.setNumber(NumberFormat.Int32LE, 1, x); + this._data.setNumber(NumberFormat.Int32LE, 1, x) } get value(): number { - return this._data.getNumber(NumberFormat.Int32LE, 5); + return this._data.getNumber(NumberFormat.Int32LE, 5) } set value(x: number) { - this._data.setNumber(NumberFormat.Int32LE, 5, x); + this._data.setNumber(NumberFormat.Int32LE, 5, x) } get toSerialNumber(): number { - return this._data.getNumber(NumberFormat.Int32LE, 9); + return this._data.getNumber(NumberFormat.Int32LE, 9) } set toSerialNumber(x: number) { - this._data.setNumber(NumberFormat.Int32LE, 9, x); + this._data.setNumber(NumberFormat.Int32LE, 9, x) } send() { - radio.sendBuffer(this._data); - basic.pause(250); + radio.sendBuffer(this._data) + basic.pause(250) } } -const playerIcons = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; +const playerIcons = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" class Player { - id: number; - icon: number; - health: HealthState; + id: number + icon: number + health: HealthState show() { - basic.showString(playerIcons[this.icon]); + basic.showString(playerIcons[this.icon]) } } // common state -let state = GameState.Stopped; +let state = GameState.Stopped // master state -let master = false; -let patientZero: Player; -const players: Player[] = []; +let master = false +let patientZero: Player +const players: Player[] = [] // player state -let paired = false; -let infectedBy = -1; // who infected (playerIcon) -let infectedTime = 0; // local time when infection happened -let playerIcon = -1; // player icon and identity -let health = HealthState.Healthy; +let paired = false +let infectedBy = -1 // who infected (playerIcon) +let infectedTime = 0 // local time when infection happened +let playerIcon = -1 // player icon and identity +let health = HealthState.Healthy // get a player instance (creates one as needed) function player(id: number): Player { for (const p of players) - if (p.id == id) return p; + if (p.id == id) return p // add player to game - let p = new Player(); - p.id = id; - p.icon = (players.length + 1) % playerIcons.length; - p.health = HealthState.Healthy; - players.push(p); + let p = new Player() + p.id = id + p.icon = (players.length + 1) % playerIcons.length + p.health = HealthState.Healthy + players.push(p) serial.writeLine(`player ==> ${p.id}`) - return p; + return p } function allDead(): boolean { for (const p of players) - if (p.health != HealthState.Dead) return false; - return true; + if (p.health != HealthState.Dead) return false + return true } function gameOver() { - state = GameState.Over; + state = GameState.Over if (patientZero) - patientZero.show(); + patientZero.show() } function gameFace() { switch (state) { case GameState.Stopped: - basic.showIcon(GameIcons.Pairing); - break; + basic.showIcon(GameIcons.Pairing) + break case GameState.Pairing: if (playerIcon > -1) - basic.showString(playerIcons[playerIcon]); + basic.showString(playerIcons[playerIcon]) else - basic.showIcon(paired ? GameIcons.Paired : GameIcons.Pairing, 1); - break; + basic.showIcon(paired ? GameIcons.Paired : GameIcons.Pairing, 1) + break case GameState.Infecting: case GameState.Running: switch (health) { case HealthState.Dead: - basic.showIcon(GameIcons.Dead, 1); - break; + basic.showIcon(GameIcons.Dead, 1) + break case HealthState.Sick: - basic.showIcon(GameIcons.Sick, 1); - break; + basic.showIcon(GameIcons.Sick, 1) + break default: - basic.showIcon(GameIcons.Healthy, 1); - break; + basic.showIcon(GameIcons.Healthy, 1) + break } - break; + break case GameState.Over: // show id - basic.showString(playerIcons[playerIcon]); - basic.pause(2000); + basic.showString(playerIcons[playerIcon]) + basic.pause(2000) // show health switch (health) { case HealthState.Dead: - basic.showIcon(GameIcons.Dead, 2000); - break; + basic.showIcon(GameIcons.Dead, 2000) + break case HealthState.Sick: - basic.showIcon(GameIcons.Sick, 2000); - break; + basic.showIcon(GameIcons.Sick, 2000) + break case HealthState.Incubating: - basic.showIcon(GameIcons.Incubating, 2000); - break; + basic.showIcon(GameIcons.Incubating, 2000) + break default: - basic.showIcon(GameIcons.Healthy, 2000); - break; + basic.showIcon(GameIcons.Healthy, 2000) + break } // show how infected if (infectedBy > -1) { - basic.showString(" INFECTED BY"); - basic.showString(playerIcons[infectedBy]); - basic.pause(2000); + basic.showString(" INFECTED BY") + basic.showString(playerIcons[infectedBy]) + basic.pause(2000) } else { - basic.showString(" PATIENT ZERO"); - basic.pause(2000); + basic.showString(" PATIENT ZERO") + basic.pause(2000) } // show score - game.showScore(); - basic.pause(1000); - break; + game.showScore() + basic.pause(1000) + break } } // master button controller -input.onButtonPressed(Button.AB, () => { +input.onButtonPressed(Button.AB, function () { // register as master if (state == GameState.Stopped && !master) { - master = true; - paired = true; - state = GameState.Pairing; - serial.writeLine("registered as master"); - radio.setTransmitPower(7); // beef up master signal - basic.showString("0"); - return; + master = true + paired = true + state = GameState.Pairing + serial.writeLine("registered as master") + radio.setTransmitPower(7) // beef up master signal + basic.showString("0") + return } - if (!master) return; // master only beyond this + if (!master) return // master only beyond this // launch game if (state == GameState.Pairing) { // pick 1 player and infect him - patientZero = players[randint(0, players.length - 1)]; + patientZero = players[randint(0, players.length - 1)] // infecting message needs to be confirmed by // the player - state = GameState.Infecting; - serial.writeLine(`game started ${players.length} players`); + state = GameState.Infecting + serial.writeLine(`game started ${players.length} players`) } // end game else if (state == GameState.Running) { - gameOver(); + gameOver() } }) -radio.setGroup(42); +radio.setGroup(42) radio.onReceivedBuffer(function (receivedBuffer: Buffer) { - const incomingMessage = new Message(receivedBuffer); - const signal = radio.receivedPacket(RadioPacketProperty.SignalStrength); + const incomingMessage = new Message(receivedBuffer) + const signal = radio.receivedPacket(RadioPacketProperty.SignalStrength) if (master) { switch (incomingMessage.kind) { case MessageKind.PairRequest: // register player - let n = players.length; - player(incomingMessage.fromSerialNumber); + let n = players.length + player(incomingMessage.fromSerialNumber) // show player number if changed if (n != players.length) { - basic.showNumber(players.length); + basic.showNumber(players.length) } - break; + break case MessageKind.HealthValue: - let p = player(incomingMessage.fromSerialNumber); - p.health = incomingMessage.value; + let p = player(incomingMessage.fromSerialNumber) + p.health = incomingMessage.value // check if all infected if (allDead()) - gameOver(); - break; + gameOver() + break } } else { switch (incomingMessage.kind) { case MessageKind.GameState: // update game state - state = incomingMessage.value as GameState; - break; + state = incomingMessage.value as GameState + break case MessageKind.InitialInfect: if (infectedBy < 0 && incomingMessage.toSerialNumber == control.deviceSerialNumber()) { // infected by master - infectedBy = 0; // infected my master - infectedTime = input.runningTime(); - health = HealthState.Incubating; - serial.writeLine(`infected ${control.deviceSerialNumber()}`); + infectedBy = 0 // infected my master + infectedTime = input.runningTime() + health = HealthState.Incubating + serial.writeLine(`infected ${control.deviceSerialNumber()}`) } - break; + break case MessageKind.HealthSet: if (incomingMessage.toSerialNumber == control.deviceSerialNumber()) { - const newHealth = incomingMessage.value; + const newHealth = incomingMessage.value if (health < newHealth) { - health = newHealth; + health = newHealth } } - break; + break case MessageKind.PairConfirmation: if (!paired && state == GameState.Pairing && incomingMessage.toSerialNumber == control.deviceSerialNumber()) { // paired! serial.writeLine(`player paired ==> ${control.deviceSerialNumber()}`) - playerIcon = incomingMessage.value; - paired = true; + playerIcon = incomingMessage.value + paired = true } - break; + break case MessageKind.TransmitVirus: if (state == GameState.Running) { if (health == HealthState.Healthy) { - serial.writeLine(`signal: ${signal}`); + serial.writeLine(`signal: ${signal}`) if (signal > RSSI && randint(0, 100) > TRANSMISSIONPROB) { - infectedBy = incomingMessage.value; - infectedTime = input.runningTime(); - health = HealthState.Incubating; + infectedBy = incomingMessage.value + infectedTime = input.runningTime() + health = HealthState.Incubating } } } - break; + break case MessageKind.HealthValue: if (health != HealthState.Dead && signal > RSSI) { - game.addScore(1); + game.addScore(1) } - break; + break } } }) // main game loop -basic.forever(() => { - let message: Message; +basic.forever(function () { + let message: Message if (master) { switch (state) { case GameState.Pairing: // tell each player they are registered for (const p of players) { - message = new Message(); - message.kind = MessageKind.PairConfirmation; - message.value = p.icon; - message.toSerialNumber = p.id; - message.send(); + message = new Message() + message.kind = MessageKind.PairConfirmation + message.value = p.icon + message.toSerialNumber = p.id + message.send() } - serial.writeLine(`pairing ${players.length} players`); - basic.pause(500); - break; + serial.writeLine(`pairing ${players.length} players`) + basic.pause(500) + break case GameState.Infecting: if (patientZero.health == HealthState.Healthy) { - message = new Message(); - message.kind = MessageKind.InitialInfect; - message.toSerialNumber = patientZero.id; - message.send(); - basic.pause(100); + message = new Message() + message.kind = MessageKind.InitialInfect + message.toSerialNumber = patientZero.id + message.send() + basic.pause(100) } else { - serial.writeLine(`patient ${patientZero.id} infected`); + serial.writeLine(`patient ${patientZero.id} infected`) // show startup - basic.showIcon(GameIcons.Dead); - state = GameState.Running; + basic.showIcon(GameIcons.Dead) + state = GameState.Running } - break; + break case GameState.Running: for (const p of players) { - message = new Message(); - message.kind = MessageKind.HealthSet; - message.value = p.health; - message.toSerialNumber = p.id; - message.send(); + message = new Message() + message.kind = MessageKind.HealthSet + message.value = p.health + message.toSerialNumber = p.id + message.send() } - break; + break case GameState.Over: if (patientZero) - patientZero.show(); - break; + patientZero.show() + break } message = new Message() - message.kind = MessageKind.GameState; - message.value = state; - message.send(); + message.kind = MessageKind.GameState + message.value = state + message.send() } else { // player loop switch (state) { case GameState.Pairing: // broadcast player id if (playerIcon < 0) { - message = new Message(); - message.kind = MessageKind.PairRequest; - message.fromSerialNumber = control.deviceSerialNumber(); - message.send(); + message = new Message() + message.kind = MessageKind.PairRequest + message.fromSerialNumber = control.deviceSerialNumber() + message.send() } else if (infectedBy > -1) { - message = new Message(); - message.kind = MessageKind.HealthValue; - message.fromSerialNumber = control.deviceSerialNumber(); - message.value = health; - message.send(); + message = new Message() + message.kind = MessageKind.HealthValue + message.fromSerialNumber = control.deviceSerialNumber() + message.value = health + message.send() } - break; + break case GameState.Infecting: - message = new Message(); - message.kind = MessageKind.HealthValue; - message.fromSerialNumber = control.deviceSerialNumber(); - message.value = health; - message.send(); - break; + message = new Message() + message.kind = MessageKind.HealthValue + message.fromSerialNumber = control.deviceSerialNumber() + message.value = health + message.send() + break case GameState.Running: // update health status if (health != HealthState.Healthy && input.runningTime() - infectedTime > DEATH) - health = HealthState.Dead; + health = HealthState.Dead else if (health != HealthState.Healthy && input.runningTime() - infectedTime > INCUBATION) - health = HealthState.Sick; + health = HealthState.Sick // transmit disease if (health == HealthState.Incubating || health == HealthState.Sick) { - message = new Message(); - message.kind = MessageKind.TransmitVirus; - message.fromSerialNumber = control.deviceSerialNumber(); - message.value = playerIcon; - message.send(); + message = new Message() + message.kind = MessageKind.TransmitVirus + message.fromSerialNumber = control.deviceSerialNumber() + message.value = playerIcon + message.send() } - message = new Message(); - message.kind = MessageKind.HealthValue; - message.fromSerialNumber = control.deviceSerialNumber(); - message.value = health; - message.send(); - break; + message = new Message() + message.kind = MessageKind.HealthValue + message.fromSerialNumber = control.deviceSerialNumber() + message.value = health + message.send() + break } // show current animation - gameFace(); + gameFace() } }) diff --git a/docs/projects/jonnys-bird.md b/docs/projects/jonnys-bird.md new file mode 100644 index 00000000000..fd26a0bee16 --- /dev/null +++ b/docs/projects/jonnys-bird.md @@ -0,0 +1,110 @@ +# Jonny's Bird + +The ``||music:play sound||`` block lets you create and play complex sounds beyond the simple sequence of tones in a melody. You can choose a sound waveform, change its frequency or volume, and add custom effects. Here's a program you can code to create fun bird sounds when you shake or tilt the @boardname@! + +## Use acceleration to set frequency + +The acceleration in the `X`and `Y` dimensions are used to set the frequencies of the sound. Make two variables named ``||variables:currFreq||`` and ``||variables:lastFreq||``. One variable will hold the value for the current freqency as an input of accleration in the `X` direction. The other will remember the previous frequency value. + +Get a ``||loops:forever||`` block and pull the ``||variables:set currFreq||`` and ``||variables:set lastFreq||`` blocks into it. Change the value for ``||variables:set lastFreq||`` from `0` to ``||variables:currFreq||``. + +```blocks +let currfreq = 0 +let lastfreq = 0 +basic.forever(function () { + currfreq = 0 + lastfreq = currfreq +}) +``` + +Pull a ``||math:map from to||`` block into the value slot of the ``||variables:set currFreq||``. Use ``||input:acceleration (mg) x||`` as the mapping value, set the `from` value range as `-1024` and `1023`. Set the `to` value range as `0` and `5000`. + +```blocks +let currfreq = 0 +let lastfreq = 0 +basic.forever(function () { + currfreq = Math.map(input.acceleration(Dimension.X), -1024, 1023, 1, 5000) + lastfreq = currfreq +}) +``` + +Go get a ``||music:play sound until done||`` block and place it in between the ``||variables:set currFreq||`` and ``||variables:set lastFreq||``. + +```blocks +let currFreq = 0 +let lastFreq = 0 +basic.forever(function () { + currFreq = Math.map(input.acceleration(Dimension.X), -1024, 1023, 0, 5000) + music.playSoundEffect(music.createSoundEffect(WaveShape.Sine, 5000, 0, 255, 0, 500, SoundExpressionEffect.None, InterpolationCurve.Linear), SoundExpressionPlayMode.UntilDone) + lastFreq = currFreq +}) +``` + +Expand the sound effect parameters in ``||music:play sound until done||`` by clicking the **(+)** symbol. Duplicate **2** ``||math:map from to||`` blocks from ``||variables:set currFreq||`` and place one in the `start frequency` value and the other in the `end frequency` value. Change the acceleration direction in the `end frequency` value to the `y` direction. + +```blocks +let currFreq = 0 +let lastFreq = 0 +basic.forever(function () { + currFreq = Math.map(input.acceleration(Dimension.X), -1024, 1023, 0, 5000) + music.playSoundEffect(music.createSoundEffect(WaveShape.Sine, + Math.map(input.acceleration(Dimension.X), -1024, 1023, 0, 5000), + Math.map(input.acceleration(Dimension.Y), -1024, 1023, 0, 5000), + 255, + 0, + 500, + SoundExpressionEffect.None, InterpolationCurve.Linear), SoundExpressionPlayMode.UntilDone) + lastFreq = currFreq +}) +``` + +## Add duration and volume + +Click the **(+)** symbol again on the sound effect block inside of ``||music:play sound until done||``. This will show the volume parameters. + +Pull out **3** ``||math:pick random||`` blocks and put them in for the values of `duration`, `start volume`, and `end volume`. For `duration`, use a range of `40` to `100`. For both `start volume` and `end volume`, use a random range of `0` to `1024`. + +```blocks +let currFreq = 0 +let lastFreq = 0 +basic.forever(function () { + currFreq = Math.map(input.acceleration(Dimension.X), -1024, 1023, 0, 5000) + music.playSoundEffect(music.createSoundEffect(WaveShape.Sine, + Math.map(input.acceleration(Dimension.X), -1024, 1023, 0, 5000), + Math.map(input.acceleration(Dimension.Y), -1024, 1023, 0, 5000), + randint(0, 1024), + randint(0, 1024), + randint(40, 100), + SoundExpressionEffect.None, InterpolationCurve.Linear), SoundExpressionPlayMode.UntilDone) + lastFreq = currFreq +}) +``` + +## Set the effects + +Once again, click the **(+)** symbol again on the sound effect block inside of ``||music:play sound until done||``. The effects parameters will appear. Change the setting for `effect` to `vibrato` and change `interpolation` to `curve`. + + +```blocks +let currfreq = 0 +let lastfreq = 0 +basic.forever(function () { + currfreq = Math.map(input.acceleration(Dimension.X), -1024, 1023, 1, 5000) + music.playSoundEffect(music.createSoundEffect( + WaveShape.Sine, + Math.map(input.acceleration(Dimension.X), -1024, 1023, 1, 5000), + Math.map(input.acceleration(Dimension.Y), -1024, 1023, 1, 5000), + randint(0, 1024), + randint(0, 1024), + randint(40, 100), + SoundExpressionEffect.Vibrato, + InterpolationCurve.Curve + ), + SoundExpressionPlayMode.UntilDone) + lastfreq = currfreq +}) +``` + +## Birds are singing! + +Transfer you program to the @boardname@, shake and tilt it. The birds are singing! \ No newline at end of file diff --git a/docs/projects/karel.md b/docs/projects/karel.md index 2e91a12c59e..e05babc7e8c 100644 --- a/docs/projects/karel.md +++ b/docs/projects/karel.md @@ -56,7 +56,7 @@ For patterns that you design, decide which LEDs you want to turn on and then mak Figure out how to make the first letter of your name with the LEDs. ```sim -basic.forever(() => { +basic.forever(function () { basic.showAnimation(` # # # . . # # # . . # # # . . # # # . . . . . . . # . . . . # . . . . # . . # . @@ -75,7 +75,7 @@ basic.forever(() => { Make something fun! ```sim -basic.forever(() => { +basic.forever(function () { basic.showAnimation(` # . . . . # . . . . # . . . . # . . . . # . . . . # . . . . . . . . . # . . . . # . . . . # . . . . # . . . # # . # # # @@ -99,86 +99,86 @@ Copy this code into the JavaScript editor and then download it to the board. ```typescript class Board { - public isKarelActive: boolean; - public karelX: number; - public karelY: number; + public isKarelActive: boolean + public karelX: number + public karelY: number - public ledState: Image; - private karelDirection: Direction; + public ledState: Image + private karelDirection: Direction constructor() { - this.isKarelActive = true; - this.karelX = 2; - this.karelY = 2; - this.karelDirection = Direction.UP; + this.isKarelActive = true + this.karelX = 2 + this.karelY = 2 + this.karelDirection = Direction.UP this.ledState = images.createImage(` . . . . . . . . . . . . . . . . . . . . . . . . . - `); + `) } pressedA() { if (!this.isKarelActive) { - return; + return } - this.karelDirection = (this.karelDirection + 1) % 4; + this.karelDirection = (this.karelDirection + 1) % 4 } pressedB() { if (!this.isKarelActive) { - return; + return } - this.ledState.setPixel(this.karelX, this.karelY, true); + this.ledState.setPixel(this.karelX, this.karelY, true) this.moveKarel() } shake() { if (!this.isKarelActive) { - return; + return } this.moveKarel() } private moveKarel() { if (!this.isKarelActive) { - return; + return } switch (this.karelDirection) { case Direction.UP: if (this.karelY > 0) { - this.karelY -= 1; + this.karelY -= 1 } - break; + break case Direction.LEFT: if (this.karelX > 0) { - this.karelX -= 1; + this.karelX -= 1 } - break; + break case Direction.DOWN: if (this.karelY < 4) { - this.karelY += 1; + this.karelY += 1 } - break; + break case Direction.RIGHT: if (this.karelX < 4) { - this.karelX += 1; + this.karelX += 1 } - break; + break } } pressedAB() { - this.isKarelActive = !this.isKarelActive; + this.isKarelActive = !this.isKarelActive } update() { - this.ledState.showImage(0); + this.ledState.showImage(0) } } -const board = new Board(); +const board = new Board() enum Direction { UP = 0, LEFT, @@ -186,21 +186,21 @@ enum Direction { RIGHT } input.onButtonPressed(Button.B, function () { - board.pressedB(); - board.update(); + board.pressedB() + board.update() }) input.onGesture(Gesture.Shake, function () { - board.shake(); - board.update(); + board.shake() + board.update() }) input.onButtonPressed(Button.A, function () { - board.pressedA(); - board.update(); + board.pressedA() + board.update() }) input.onButtonPressed(Button.AB, function () { - board.pressedAB(); - board.update(); + board.pressedAB() + board.update() }) basic.forever(function () { if (board.isKarelActive) { diff --git a/docs/projects/level.md b/docs/projects/level.md index 240465bab5e..ce21bcd0059 100644 --- a/docs/projects/level.md +++ b/docs/projects/level.md @@ -1,13 +1,13 @@ # Level -## Introduction @unplugged +## Is it level? @unplugged Is your table flat? Use the @boardname@ as a level! ![A level drawing](/static/mb/projects/level.png) -## Step 1 +## {Step 1} Make a variable ``||variables:x||`` and store the ``||input:acceleration x||`` value in the ``||basic:forever||`` loop. @@ -19,7 +19,7 @@ basic.forever(function() { }) ``` -## Step 2 +## {Step 2} Make another variable ``||variables:y||`` and store the ``||input:acceleration y||`` value. @@ -31,7 +31,7 @@ basic.forever(function() { }) ``` -## Step 3 +## {Step 3} Add a code to test ``||logic:if||`` the ``||Math:absolute value||`` of ``||variables:x||`` is ``||logic:greater than||`` ``32``. If it is true, ``||basic:show an icon||`` to tell you that the @boardname@ is not flat, ``||logic:else||`` show nothing, for now. @@ -49,7 +49,7 @@ basic.forever(function() { }) ``` -## Step 4 +## {Step 4} Add an ``||logic:else if||`` to check that the ``||Math:absolute value||`` of ``||variables:y||`` is ``||logic:greater than||`` ``32``. If it is true, ``||basic:show an icon||`` that tells you the @boardname@ is not flat. @@ -69,7 +69,7 @@ basic.forever(function() { }) ``` -## Step 5 +## {Step 5} The code under the ``||logic:else||`` will run if both acceleration ``x`` and ``y`` are small, which happens when the @boardname@ is laying flat. Add code to ``||basic:show a happy image||``. @@ -88,8 +88,11 @@ basic.forever(function() { }) ``` -## Step 6 +## {Step 6} If you have a @boardname@ connected, click ``|Download|`` to transfer your code! Try it out on a table, counter, or window sill in your house! +```template +basic.forever(function() {}) +``` \ No newline at end of file diff --git a/docs/projects/light-level-meter.md b/docs/projects/light-level-meter.md index da3c9f92836..6263c7571e7 100644 --- a/docs/projects/light-level-meter.md +++ b/docs/projects/light-level-meter.md @@ -7,7 +7,7 @@ to detect the amount of light. ## Save a reading -Create a variable, ``||variables:reading||``, to set to the current ``||input:light level||`` inside the ``||loops:forever||`` loop. +Create a variable, ``||variables:reading||``, to set to the current ``||input:light level||`` inside the ``||basic:forever||`` loop. ```blocks let reading = 0 diff --git a/docs/projects/light-monster/code.md b/docs/projects/light-monster/code.md index 07a0c7d9bc0..5eac96e2e76 100644 --- a/docs/projects/light-monster/code.md +++ b/docs/projects/light-monster/code.md @@ -13,7 +13,7 @@ Add code to open the mouth when light is detected. We are going to add code to open the mouth proportionally to the amount of light on the @boardname@. The code is in a loop so we'll continually read the light level and map it to an angle using the ``||pins:map||`` function. ```blocks -basic.forever(() => { +basic.forever(function () { pins.servoWritePin(AnalogPin.P0, pins.map( input.lightLevel(), 0, diff --git a/docs/projects/light-monster/connect.md b/docs/projects/light-monster/connect.md index e3064d3ac54..d697aaead1c 100644 --- a/docs/projects/light-monster/connect.md +++ b/docs/projects/light-monster/connect.md @@ -11,13 +11,13 @@ Remote control your monster with another @boardname@. You will need one more @boardname@ for this part. By using the radio, we can control the monster with another @boardname@. Download the code below to the @boardname@ on the monster and then again onto a "controller" @boardname@. Whenever button **A** is pressed, the monster's mouth moves once. ```blocks -radio.onReceivedNumber(({ receivedNumber }) => { +radio.onReceivedNumber(function(receivedNumber) { pins.servoWritePin(AnalogPin.P0, 30) basic.pause(500) pins.servoWritePin(AnalogPin.P0, 150) basic.pause(500) }) -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { radio.sendNumber(0) }) ``` diff --git a/docs/projects/love-meter.md b/docs/projects/love-meter.md index 2cd5d88a22d..8055f53b244 100644 --- a/docs/projects/love-meter.md +++ b/docs/projects/love-meter.md @@ -1,44 +1,61 @@ # Love Meter -## Introduction @unplugged +## {Introduction @unplugged} -Make a love meter, how sweet! The @boardname@ is feeling the love, then sometimes not so much! +How much love 😍 are you emitting today? Create a 💓 LOVE METER 💓 machine with your micro:bit! ![Love meter banner message](/static/mb/projects/love-meter/love-meter.gif) -## Step 1 +## {Step 1} -Let's build a **LOVE METER** machine. Place an ``||input:on pin pressed||`` block to run code when pin **0** is pressed. Use ``P0`` from the list of pin inputs. +We'll use this ``||input:on pin pressed||`` block to run code when pin **0** on the micro:bit is pressed. From the ``||basic:Basic||`` Toolbox category, drag a ``||basic:show number||`` block and drop into the ``||input:on pin pressed||`` block. ```blocks -input.onPinPressed(TouchPin.P0, () => { -}); +input.onPinPressed(TouchPin.P0, function() { + //@highlight + basic.showNumber(0) +}) ``` -## Step 2 +## {Step 2} -Using ``||basic:show number||`` and ``||Math:pick random||`` blocks, show a random number from `0` to `100` when pin **0** is pressed. +From the ``||math:Math||`` category, get a ``||Math:pick random||`` block and drop it into the ``||basic:show number||`` block replacing 0. ```blocks -input.onPinPressed(TouchPin.P0, () => { - basic.showNumber(randint(0, 100)); -}); +input.onPinPressed(TouchPin.P0, function() { + //@highlight + basic.showNumber(randint(0, 100)) +}) ``` -## Step 3 -Click on pin **0** in the simulator and see which number is chosen. +## {Step 3} -## Step 4 - -Show ``"LOVE METER"`` on the screen when the @boardname@ starts. +Now let's be sure to label our Love Machine! From the ``||basic:Basic||`` Toolbox category, drag an ``||basic:on start||`` block and drop it anywhere on the Workspace. Then get a ``||basic:show string||`` block and place it in the ``||basic:on start||`` block. Type the words "LOVE METER" into the ``||basic:show string||`` block. ```blocks -basic.showString("LOVE METER"); -input.onPinPressed(TouchPin.P0, () => { - basic.showNumber(randint(0, 100)); -}); +//@highlight +basic.showString("LOVE METER") +input.onPinPressed(TouchPin.P0, function() { + basic.showNumber(randint(0, 100)) +}) ``` -## Step 5 +## {Step 4} + +Let's test our code. Press **Pin 0** on the micro:bit on-screen simulator (bottom left). Numbers between 0-25 = 🖤 No Love, 26-50 = đŸĢļ BFF Love, 51-75 = 💘 Brokenhearted Love, 76-100 = 💖đŸ”Ĩ Fiery Hot Love! + +## {Step 5} -Click ``|Download|`` to transfer your code in your @boardname@. Hold the **GND** pin with one hand and press pin **0** with the other hand to trigger this code. +If you have a @boardname@ device, connect it to your computer and click the ``|Download|`` button. Follow the instructions to transfer your code onto the @boardname@. Once your code has been downloaded, hold the **GND** pin with one hand and touch the **0** pin with the other hand. Your micro:bit 💓 LOVE METER 💓 machine will detect the love current flowing through your body! + +```blockconfig.global +randint(0, 100) +``` + +```validation.global +# BlocksExistValidator +``` + +```template +input.onPinPressed(TouchPin.P0, function() {}) +``` diff --git a/docs/projects/magic-button-trick.md b/docs/projects/magic-button-trick.md index 0300972b174..b17817bf5f3 100644 --- a/docs/projects/magic-button-trick.md +++ b/docs/projects/magic-button-trick.md @@ -2,14 +2,12 @@ ## ~avatar avatar -Build a magic trick that uses the @boardname@'s compass to detect a nearby magnet! +Build a magic trick that uses the @boardname@'s magnetometer to detect a nearby magnet! ## ~ This is a simple magic trick you can perform to amaze your friends! When you move the sticky labels on your @boardname@'s **A** and **B** button, you appear to make the buttons really switch over. To see the trick performed watch the video below. -https://youtu.be/-9KvmPopov8 - ## How the trick works The **magic** here is really in the code. This trick uses a magnet, hidden in your hand, to tell the @boardname@ to swap over the buttons. When the magnet is near the @boardname@, the **A** button starts working like the **B** button and the **B** button starts working like the **A** button. Tricky! @@ -25,17 +23,17 @@ The only things you need for this trick are your @boardname@ and any magnet that Before we code the trick itself, we need to get the buttons working as you would expect them to such that pressing button **A** displays 'A' and pressing button **B** displays 'B': ```blocks -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { basic.showString("A") }) -input.onButtonPressed(Button.B, () => { +input.onButtonPressed(Button.B, function () { basic.showString("B") }) ``` ## Step 2: Measuring magnetic force -We will use the @boardname@'s compass to detect the magnet. A compass tells us which direction we are pointing to by detecting the Earth's magnetic field, but it can also detect any other magnet nearby. We will use that to check if our magnet is next to the @boardname@ by using the ``||input:magnetic force||`` block found in the **Input** menu's **... More** section. Since we only want to measure the strength we change the drop down to select `strength`: +We will use the @boardname@'s magnetometer to detect the magnet. We will use it to check if our magnet is next to the @boardname@ by using the ``||input:magnetic force||`` block found in the **Input** menu's **... More** section. Since we only want to measure the strength we change the drop down to select `strength`: ```block let force = input.magneticForce(Dimension.Strength) @@ -50,7 +48,7 @@ If you've ever played with magnets you know they have two ends, often called a N So, in the code below, we will check if the absolute value of our magnetic field strength reading is more than `100` and save the result of that check in a new variable called ``isSwitched``: ```blocks -let force = Math.abs(input.magneticForce(Dimension.Strength)); +let force = Math.abs(input.magneticForce(Dimension.Strength)) let isSwitched = force > 100 ``` ## Step 4: Running our 'magnet nearby' check all the time @@ -58,10 +56,10 @@ let isSwitched = force > 100 At the moment, our code to detect a magnet being nearby will only run once. We need to put it into a ``||basic:forever||`` loop so that it keeps running again and again, checking for the magnet to come near to the @boardname@. We should also make sure ``isSwitched`` is set to `false` when our program starts. ```blocks -let force = 0; -let isSwitched = false; -basic.forever(() => { - force = Math.abs(input.magneticForce(Dimension.Strength)); +let force = 0 +let isSwitched = false +basic.forever(function () { + force = Math.abs(input.magneticForce(Dimension.Strength)) isSwitched = force > 100 }) ``` @@ -71,21 +69,21 @@ basic.forever(() => { Now we can check the value of our variable ``isSwitched`` whenever we want and we will know that the magnet is nearby if it's value is `true`. Let's use that to change how the buttons work and complete the code for our trick. We will add an ``||logic:if then else||`` block to each button's code and check if we should swap over what's displayed for each button if ``isSwitched`` is equal to `true`: ```blocks -let force = 0; -let isSwitched = false; -basic.forever(() => { - force = Math.abs(input.magneticForce(Dimension.Strength)); +let force = 0 +let isSwitched = false +basic.forever(function () { + force = Math.abs(input.magneticForce(Dimension.Strength)) isSwitched = force > 100 }) -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { if (isSwitched) { basic.showString("B") } else { basic.showString("A") } }) -input.onButtonPressed(Button.B, () => { +input.onButtonPressed(Button.B, function () { if (isSwitched) { basic.showString("A") } else { @@ -98,8 +96,6 @@ input.onButtonPressed(Button.B, () => { Now you just need to program your own @boardname@ and practice the trick a few times before performing for your friends. Try asking your friends to click the buttons after you have switched the labels and the trick won't work for them because they don't have a hidden magnet in their hand! -Remember, that as we are using @boardname@'s compass, it will need to be [calibrated](https://support.microbit.org/support/solutions/articles/19000008874-calibrating-the-micro-bit-compass-what-does-it-mean-when-the-micro-bit-says-draw-a-circle-or-tilt) each time we flash the program or run it for the first time. - ## About the authors This project was contributed by Brian and Jasmine Norman, aka [@MicroMonstersUK](https://twitter.com/MicroMonstersUK). You can checkout their [MicroMonsters](https://www.youtube.com/channel/UCK2DviDexh_Er2QYZerZyZQ) tutorials channel on YouTube for more projects. diff --git a/docs/projects/micro-chat.md b/docs/projects/micro-chat.md index 82e195c94f0..0af2d710eda 100644 --- a/docs/projects/micro-chat.md +++ b/docs/projects/micro-chat.md @@ -1,34 +1,59 @@ # Micro Chat -## Introduction @unplugged +## {Introduction @unplugged} ![Two @boardname@ connected via radio](/static/mb/projects/a9-radio.png) -Use the **radio** to send and receive messages with other @boardname@. +Use the micro:bit đŸ“ģ radio to send and receive đŸ’Ŧ messages between micro:bits! -## Sending a message +## {Step 1} -Use ``||input:on button pressed||`` to send a text message over radio with ``||radio:send string||``. -Every @boardname@ nearby will receive this message. +From the ``||radio:Radio||`` Toolbox category, drag a ``||radio:radio set group||`` block into the ``||basic:on start||`` block. This will act as the channel over which we'll send messages. Only micro:bits who are in the same group will be able to send and receive messages between them. + +```blocks +radio.setGroup(1) +``` + +## {Step 2} + +From the ``||input:Input||`` Toolbox category, drag an ``||input:on button A pressed||`` block onto the Workspace. + +```blocks +input.onButtonPressed(Button.A, function() {}) +``` + +## {Step 3} + +From the ``||radio:Radio||`` category, drag a ``||radio:radio send string||`` block into the ``||input:on button A pressed||`` block and type a message. When we press button A on our micro:bit, we'll send this message to every micro:bit nearby in group 1. ```blocks input.onButtonPressed(Button.A, function() { - radio.sendString(":)"); -}); + radio.sendString("Micro Chat!") +}) +``` + +## {Step 4} + +From the ``||radio:Radio||`` category, drag an ``||radio:on radio received string||`` block onto the Workspace. + +```blocks +radio.onReceivedString(function (receivedString) { +}) ``` -## Receiving a message +## {Step 5} -Add a ``||radio:on received string||`` block to run when a message is received. +From the ``||basic:Basic||`` category, get a ``||basic:show string||`` block and drop it in the ``||radio:on radio received string||`` block. ```blocks radio.onReceivedString(function (receivedString) { + basic.showString("Hello!"); }) ``` -## Displaying text +## {Step 6} -Add a ``||basic:show string||`` to display the string on the screen. Pull the ``||variables:receivedString||`` out of ``||radio:on received string||`` and put it into ``||basic:show string||``. +Pull the ``||variables:receivedString||`` variable block out of the ``||radio:on received string||`` block and put it into the ``||basic:show string||`` block replacing "Hello!" ```blocks radio.onReceivedString(function (receivedString) { @@ -36,29 +61,29 @@ radio.onReceivedString(function (receivedString) { }) ``` -## Testing in the simulator +## {Step 7} -Press button **A** on the simulator, you will notice that a second @boardname@ appears (if your screen is too small, the simulator might decide not to show it). Try pressing **A** again and notice that the ":)" message gets displayed on the other @boardname@. +Let's test our code! In the micro:bit on-screen simulator, press button **A**. You should see a second @boardname@ appear. Now try pressing **A** again. Do you see your message appear on the second micro:bit? ⭐ Great job! ⭐ ```blocks input.onButtonPressed(Button.A, function() { - radio.sendString(":)"); -}); + radio.sendString("Micro Chat!"); +}) radio.onReceivedString(function (receivedString) { basic.showString(receivedString); }) ``` -## Try it for real +## {Step 8} -If you have two @boardname@s, download the program to each one. Press button **A** on one and see if the other gets a message. +If you have a @boardname@ device, connect it to your computer and click the ``|Download|`` button. Follow the instructions to transfer your code onto the @boardname@. If you have two micro:bits, download the program to each one. Press button **A** on one and see if the other gets the message! -## Groups +## {Step 9} -Use the ``||radio:set group||`` block to assign a **group** number to your program. You will only receive messages from @boardname@s within the same group. Use this to avoid receiving messages from every @boardname@ that is transmitting. +Go further - try using different buttons to send a mix of messages 📝, or send secret 🔒 messages to different radio groups! -```blocks -radio.setGroup(123) +```template +// ``` ```package diff --git a/docs/projects/micro-coin.md b/docs/projects/micro-coin.md index 2f1e47a1273..1de98de8e8b 100644 --- a/docs/projects/micro-coin.md +++ b/docs/projects/micro-coin.md @@ -6,27 +6,31 @@ Have you heard about BitCoin and all those new Crypto currencies? Well micro:bit ## ~ +![micro:coin at CoinBank](/static/mb/projects/micro-coin/coinbank.png) + ## How does a @boardname@ make coins? -Each @boardname@ contains a **blockchain**, a sequence of **blocks**, that is public and can't be modified. Each block represents a **coin**. To mine new coins, the user shakes -the @boardname@ and, if they are in luck, their coin added to the chain as a new block! +Each @boardname@ contains a **blockchain**, a sequence of **blocks** (in this case, chunks of information not blocks of a program), that is public and can't be modified. Each block represents a **coin**. The process of making coins is called _mining_. To mine new coins, the user shakes +the @boardname@ and, if they are in luck, their coin is added to the chain as a new block! Once the block is added, it is broadcasted to the other @boardname@ (the block chain is public and can't be modified so it's ok to share it). Other @boardname@s receive the block, validate the transaction and update their block chain as needed. -Pressing ``A`` shows the number of block you added to the chain, that's your score. -Pressing ``B`` shows you the length of the chain. - -Happy mining! - ## Coins, blocks, chains A _blockchain_ is a list of _blocks_ that record transactions of a crypto-currency like BitCoin. A block might contain information like the time it was created (mined) and who mined it. The most important part of the block is it's _hash_. This is a special number made from the information in the last block of the block list combined with the hash number of previous block in the list. The new block contains information for the current transaction and this new hash number. The new block is added to the list of previous blocks. This list is then transmitted to the crypto currency network. It's really hard (like impossible) to tamper or forge a hash which allows the blockchain to be transmitted publicly. +## Start mining + +Pressing ``A`` shows the number of blocks you added to the chain, that's your score. +Pressing ``B`` shows you the length of the chain. + +Happy mining! + ### ~ hint #### Secure your coins -Build yourself a [@boardname@ wallet](/projects/wallet) to hold your coins! +Keep your coins safe. Build yourself a [@boardname@ wallet](/projects/wallet) to hold your coins! ### ~ @@ -39,7 +43,7 @@ The code uses blocks from the [radio-blockchain](https://makecode.microbit.org/p ```blocks // shaking is mining... -input.onGesture(Gesture.Shake, () => { +input.onGesture(Gesture.Shake, function () { led.stopAnimation() basic.clearScreen() basic.pause(200) // display a short pause @@ -54,7 +58,7 @@ input.onGesture(Gesture.Shake, () => { }) // show my coins -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { led.stopAnimation() let coins = blockchain.valuesFrom(blockchain.id()).length; basic.showNumber(coins); @@ -62,7 +66,7 @@ input.onButtonPressed(Button.A, () => { }) // show the block chain size -input.onButtonPressed(Button.B, () => { +input.onButtonPressed(Button.B, function () { led.stopAnimation() basic.showNumber(blockchain.length()); basic.showString("BLOCKS"); diff --git a/docs/projects/milk-carton-robot/code.md b/docs/projects/milk-carton-robot/code.md index ac5e97f684b..2a0729e0278 100644 --- a/docs/projects/milk-carton-robot/code.md +++ b/docs/projects/milk-carton-robot/code.md @@ -18,10 +18,10 @@ https://youtu.be/m-HS8OyS0pw ## Step 2: code light sensor -Code the lightsensor on the @boardname@ to control the servo. +Code the light sensor on the @boardname@ to control the servo. ```blocks -basic.forever(() => { +basic.forever(function () { led.plotBarGraph( input.lightLevel(), 0 @@ -48,7 +48,7 @@ angle range, ``[closed, opened]`` using ``pins.map``. let angle = 0 let closed = 0 let opened = 0 -basic.forever(() => { +basic.forever(function () { led.plotBarGraph( input.lightLevel(), 0 diff --git a/docs/projects/milk-carton-robot/connect.md b/docs/projects/milk-carton-robot/connect.md index 3abb7fe091f..b1371bf8a3f 100644 --- a/docs/projects/milk-carton-robot/connect.md +++ b/docs/projects/milk-carton-robot/connect.md @@ -12,13 +12,13 @@ You will need 2 @boardname@ for this part. By using the radio, we can make the M Download the code below to the @boardname@ on the Milk Carton Monster and another "controller" @boardname@. Whenever ``A`` is pressed, the Milk Carton Monster will move once. ```blocks -radio.onReceivedNumber((receivedNumber) => { +radio.onReceivedNumber(function(receivedNumber) { pins.servoWritePin(AnalogPin.P0, 0) basic.pause(500) pins.servoWritePin(AnalogPin.P0, 180) basic.pause(500) }) -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { radio.sendNumber(0) }) ``` diff --git a/docs/projects/milky-monster/code.md b/docs/projects/milky-monster/code.md index c408a24401a..351e7afe91b 100644 --- a/docs/projects/milky-monster/code.md +++ b/docs/projects/milky-monster/code.md @@ -17,11 +17,11 @@ In order for the Milky Monster to move, the @boardname@ needs to command the ser - Press button ``B`` to switch the servo to 0 degrees (to open the mouth of Milky Monster). ```blocks -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { pins.servoWritePin(AnalogPin.P0, 180) basic.showNumber(180) }) -input.onButtonPressed(Button.B, () => { +input.onButtonPressed(Button.B, function () { pins.servoWritePin(AnalogPin.P0, 0) basic.showNumber(0) }) @@ -64,7 +64,7 @@ https://youtu.be/fAR58GJUZdM Code the light sensor on the @boardname@ to control the servo. ```blocks -basic.forever(() => { +basic.forever(function () { pins.servoWritePin(AnalogPin.P0, input.lightLevel()) led.plotBarGraph( input.lightLevel(), diff --git a/docs/projects/milky-monster/connect.md b/docs/projects/milky-monster/connect.md index 8cc8deb8973..8eb03cbc2c6 100644 --- a/docs/projects/milky-monster/connect.md +++ b/docs/projects/milky-monster/connect.md @@ -12,13 +12,13 @@ You will need a second @boardname@ for this part. By using the radio, we can con Download the code below to the @boardname@ that's on the Milky Monster and again to another "controller" @boardname@. Whenever button **A** is pressed, the Milky Monster will move one time. ```blocks -radio.onReceivedNumber((receivedNumber) => { +radio.onReceivedNumber(function(receivedNumber) { pins.servoWritePin(AnalogPin.P0, 0) basic.pause(500) pins.servoWritePin(AnalogPin.P0, 180) basic.pause(500) }) -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { radio.sendNumber(0) }) ``` diff --git a/docs/projects/mood-radio.md b/docs/projects/mood-radio.md index 3c2fa2a8f89..cfe45d9e60e 100644 --- a/docs/projects/mood-radio.md +++ b/docs/projects/mood-radio.md @@ -15,7 +15,8 @@ between @boardname@s using the radio antenna, just like a phone can send text me Let's add blocks that send a number when button ``A`` is pressed. We assume that `0` is the "mood code" to send for **smiley**. ```blocks -input.onButtonPressed(Button.A, () => { +radio.setGroup(1) +input.onButtonPressed(Button.A, function () { radio.sendNumber(0) basic.showIcon(IconNames.Happy) }) @@ -40,13 +41,13 @@ radio.onReceivedNumber(function (receivedNumber) { Adding another mood to our messaging app done in a similar way. We decide that the "mood code" of `1` means **frowny**. We can add a ``B`` button event that sends that code. ```blocks -input.onButtonPressed(Button.B, () => { +input.onButtonPressed(Button.B, function () { radio.sendNumber(1) basic.showIcon(IconNames.Sad) }) ``` -If the ``||radio:on received number||`` block, we add another conditional ``||logic:if then||`` statement to handle the **frowny** "mood code". +If the ``||radio:on received number||`` event happens, we add in another conditional ``||logic:if then||`` statement to handle the **frowny** "mood code". ```blocks radio.onReceivedNumber(function (receivedNumber) { @@ -65,14 +66,15 @@ That's it. Download your code to multiple @boardname@s and try it out! Try adding a new code and use the ``||input:on shake||`` event to send it. -## Full sources +## Complete program ```blocks -input.onButtonPressed(Button.A, () => { +radio.setGroup(1) +input.onButtonPressed(Button.A, function () { radio.sendNumber(0) basic.showIcon(IconNames.Happy) }) -input.onButtonPressed(Button.B, () => { +input.onButtonPressed(Button.B, function () { radio.sendNumber(1) basic.showIcon(IconNames.Sad) }) diff --git a/docs/projects/multi-dice.md b/docs/projects/multi-dice.md index d1bfa3d9699..4f69845790a 100644 --- a/docs/projects/multi-dice.md +++ b/docs/projects/multi-dice.md @@ -1,6 +1,6 @@ # Multi Dice -## Introduction @unplugged +## {Introduction @unplugged} ![Multiple @boardname@ throwing a dice](/static/mb/projects/multi-dice.png) @@ -8,7 +8,7 @@ Build a multi-player dice game using the **radio**. The **radio** blocks let you In this game, you shake to "throw the dice" and send the result to the other @boardname@. If you receive a result of a dice throw equal or greater than yours, you lose. -## Dice game +## {Dice game} Let's start by rebuilding the **dice** game. If you are unsure about the details, try the **dice** tutorial again. @@ -18,13 +18,13 @@ input.onGesture(Gesture.Shake, function () { }) ``` -## Dice variable +## {Dice variable} We need to store the result of the dice cast in a variable. A **variable** is like a place in the memory of the @boardname@ where you save information, like numbers. -* Go to the **Variables** toolbox and click ``||Make a Variable||`` to create a new variable. We will call it **dice**. -* Add a ``||set dice to||`` block and drag the ``||pick random||`` into it. -* Drag a ``||dice||`` from the **Variables** toolbox into the ``||basic:show number||`` block. +* Go to the **Variables** toolbox and click ``Make a Variable`` to create a new variable. We will call it **dice**. +* Add a ``||variables:set dice to||`` block and drag the ``||math:pick random||`` into it. +* Drag a ``||variables:dice||`` variable from the **Variables** toolbox into the ``||basic:show number||`` block. ```blocks let dice = 0 @@ -34,11 +34,12 @@ input.onGesture(Gesture.Shake, function () { }) ``` -## Send the dice +## {Send the dice} -Put in a ``||radio:send number||`` and a ``||dice||`` to send the value stored in the ``dice`` variable via radio. +Put in a ``||radio:send number||`` and a ``||variables:dice||`` to send the value stored in the ``||variables:dice||`` variable via radio. Make sure to add a ``||radio:set group||`` to ``||basic:on start||`` with the group number set to the group you want to use. ```blocks +radio.setGroup(1) let dice = 0 input.onGesture(Gesture.Shake, function () { dice = randint(1, 6) @@ -47,18 +48,18 @@ input.onGesture(Gesture.Shake, function () { }) ``` -## Receive the dice +## {Receive the dice} -Go get an ``||radio:on received number||`` event block. This event runs when a radio message from another @boardname@ arrives. The ``receivedNumber`` value is the value of the dice in this game. +Go get an ``||radio:on received number||`` event block. This event runs when a radio message from another @boardname@ arrives. The ``||variables:receivedNumber||`` value is the value of the dice in this game. ```blocks radio.onReceivedNumber(function (receivedNumber) { }) ``` -## Check your cast +## {Check your cast} -Add a ``||logic:if||`` block to test if ``receivedNumber`` is greater or equal to ``dice``. +Add a ``||logic:if||`` block to test if ``||variables:receivedNumber||`` is greater or equal to ``||variables:dice||``. If is, you lost so display a sad face on the screen. ```blocks @@ -70,7 +71,7 @@ radio.onReceivedNumber(function (receivedNumber) { }) ``` -## Test it! +## {Test it!} Try pressing **SHAKE** in the simulator and see that a second @boardname@ appears. You can play the game on both virtual boards. @@ -78,6 +79,7 @@ If you have more than one @boardname@, download your code onto each one and try ```blocks let dice = 0 +radio.setGroup(1) input.onGesture(Gesture.Shake, function () { dice = randint(1, 6) basic.showNumber(dice) diff --git a/docs/projects/music.md b/docs/projects/music.md index cb59128341e..58e2609d928 100644 --- a/docs/projects/music.md +++ b/docs/projects/music.md @@ -20,5 +20,15 @@ Get your headphone and let's do music! "url":"/projects/guitar", "description": "An awesome cardboard guitar project, get ready to riff!", "imageUrl":"/static/mb/projects/guitar.png" +}, { + "name": "Jonny's Bird", + "url":"/projects/jonnys-bird", + "description": "Shake and tilt to make incredible bird sounds!", + "imageUrl":"/static/mb/projects/jonnys-bird.png" +}, { + "name": "Electric Guitar", + "url":"/projects/electric-guitar", + "description": "Make an electric guitar that you can play real chords with using the micro:bit!", + "imageUrl":"/static/mb/projects/electric-guitar.png" }] ``` diff --git a/docs/projects/name-tag.md b/docs/projects/name-tag.md index 85ca96b3445..82575927e79 100644 --- a/docs/projects/name-tag.md +++ b/docs/projects/name-tag.md @@ -1,36 +1,35 @@ # Name Tag -## Introduction @unplugged +## Turn your micro:bit into a digital name tag @unplugged -Tell everyone who you are. Show you name on the LEDs. +See your name in 💡 lights! 💡 Code the micro:bit to scroll your name across the screen. ![Name scrolling on the LEDs](/static/mb/projects/name-tag/name-tag.gif) -## Step 1 +## {Step 1} -Place the ``||basic:show string||`` block in the ``||basic:forever||`` block to repeat it. Change the text to your name. +Click on the ``||basic:Basic||`` category in the Toolbox. +Drag a ``||basic:show string||`` block into the ``||basic:forever||`` block. +Then in the ``||basic:show string||`` block, change the text from "Hello!" to your name. ```blocks -basic.forever(() => { - basic.showString("MICRO"); -}); +basic.forever(function() { + basic.showString("My Name"); +}) ``` -## Step 2 +## {Step 2} -Look at the simulator and make sure it shows your name on the screen. +Look at the @boardname@ simulator on the screen. Do you see your name scrolling across? ⭐ Great job! ⭐ You've turned the micro:bit into a digital name tag! -## Step 3 +## {Step 3} -Place more ``||basic:show string||`` blocks to create your own story. +If you have a @boardname@ device, connect it to your computer and click the ``|Download|`` button. Follow the instructions to transfer your code onto the @boardname@ and watch your name appear in lights! -```blocks -basic.forever(() => { - basic.showString("MICRO"); - basic.showString("<3<3<3"); -}) -``` +## {Step 4} -## Step 4 +Go further - try adding more ``||basic:show string||`` blocks to create a story! Learn more about how the @boardname@ lights work by watching [this video](https://youtu.be/qqBmvHD5bCw). -If you have a @boardname@ connected, click ``|Download|`` to transfer your code and watch your name scroll! +```template +basic.forever(function() {}) +``` \ No newline at end of file diff --git a/docs/projects/plant-watering/code.md b/docs/projects/plant-watering/code.md index 73ba7246207..b6d7cc41b1b 100644 --- a/docs/projects/plant-watering/code.md +++ b/docs/projects/plant-watering/code.md @@ -30,7 +30,7 @@ radio.setTransmitSerialNumber(true) radio.setGroup(4) led.setBrightness(64) let reading = 0 -basic.forever(() => { +basic.forever(function () { pins.analogWritePin(AnalogPin.P1, 1023) reading = pins.analogReadPin(AnalogPin.P0) radio.sendNumber(reading / 4); diff --git a/docs/projects/plot-acceleration.md b/docs/projects/plot-acceleration.md index d92cb42cd49..5be08687bdd 100644 --- a/docs/projects/plot-acceleration.md +++ b/docs/projects/plot-acceleration.md @@ -6,7 +6,7 @@ The ``||led:plot bar graph||`` uses the screen to display the _magnitude_ (how b ## Acceleration -In a ``||loops:forever||`` loop, ``||led:plot||`` ``||input:acceleration||`` in the ``x`` dimension on the LEDs. +In a ``||basic:forever||`` loop, ``||led:plot||`` ``||input:acceleration||`` in the ``x`` dimension on the LEDs. ```blocks basic.forever(function() { @@ -19,7 +19,17 @@ basic.forever(function() { ## Console -Click on the **Show Console** button by the simulator to see a chart of the values plotted by the block over a period of time. Hover over the board in the simulator to make a force in the ``x`` dimension. +Click on the **(+)** in the ``||led:plot bar graph||`` block to expand it. The ``||led:serial write||`` parameter will appear and is set to **ON**. Now, after a moment, the **Show data Simulator** button will show up near the simulator. Click on it to see a chart of the values plotted by the block over a period of time. Hover over the board in the simulator to make a force in the ``x`` dimension. + +```blocks +basic.forever(function() { + led.plotBarGraph( + input.acceleration(Dimension.X), + 0, + true + ) +}) +``` ## Maximum value @@ -30,24 +40,30 @@ For example, we can tell the block that we don't expect values beyond ``1000`` m basic.forever(function() { led.plotBarGraph( input.acceleration(Dimension.X), - 1000 + 1000, + true ) }) ``` ## Other sensors -You can use this block for pretty much any kind of data. Try it out! Plot the ``||input:light level||`` inside the ``||loops:forever||`` instead. Play with the light sensor in the simulator. +You can use this block for pretty much any kind of data. Try it out! Plot the ``||input:light level||`` inside the ``||basic:forever||`` instead. Play with the light sensor in the simulator. ```blocks basic.forever(function() { led.plotBarGraph( input.lightLevel(), - 0 + 0, + true ) }) ``` ## Download and try -Download the code to your @boardname@ and test the sensors. \ No newline at end of file +Download the code to your @boardname@ and test the sensors. + +```template +basic.forever(function() {}) +``` \ No newline at end of file diff --git a/docs/projects/puma-rs-computer-shoe.md b/docs/projects/puma-rs-computer-shoe.md index ba60efe70a6..9feb3859905 100644 --- a/docs/projects/puma-rs-computer-shoe.md +++ b/docs/projects/puma-rs-computer-shoe.md @@ -21,7 +21,7 @@ To compute the distance, the engineers relied on the relationship between stride [![Screenshot of the US patent](/static/mb/projects/puma-rs-computer-shoe/uspatent.png)](/static/mb/projects/puma-rs-computer-shoe/patent.pdf) -Assuming``T`` is the elapsed time, ``S`` is the number of foot strikes +Assuming ``T`` is the elapsed time, ``S`` is the number of foot strikes and ``A``, ``B`` are constants that have been identified in the calibration phase, the speed ``V`` is computed as follows: diff --git a/docs/projects/python/smiley-buttons.md b/docs/projects/python/smiley-buttons.md new file mode 100644 index 00000000000..9633cd464ca --- /dev/null +++ b/docs/projects/python/smiley-buttons.md @@ -0,0 +1,57 @@ +# Smiley Buttons + +### @explicitHints true + +## Code a micro:bit emoji! @unplugged + +Code the buttons on the @boardname@ to show that it's happy or sad. +(Want to learn how the buttons works? [Watch this video](https://youtu.be/t_Qujjd_38o)). + +![Pressing the A and B buttons](/static/mb/projects/smiley-buttons/sim.gif) + +## {Step 1} + +Put in an ``||input:on button pressed||`` event to run code when button **A** is pressed. + +```python +def on_button_pressed_a(): + pass +input.on_button_pressed(Button.A, on_button_pressed_a) +``` + +## {Step 2} + +Use ``||basic:show icon||`` to display a **Happy** face on the screen. + +Press the **A** button in the simulator to see the smiley. + +```python +def on_button_pressed_a(): + basic.show_icon(IconNames.HAPPY) +input.on_button_pressed(Button.A, on_button_pressed_a) +``` + +## {Step 3} + +Use another ``||input:on button pressed||`` with a ``||basic:show icon||`` inside to display a **Sad** face when button **B** is pressed. + +```python +def on_button_pressed_a2(): + basic.show_icon(IconNames.SAD) +input.on_button_pressed(Button.B, on_button_pressed_a2) +``` + +## {Step 4} + +Add a secret mode that happens when **A** and **B** are pressed together. For this case, use ``||basic:show icon||`` multiple times to create an animation. + +```python +def on_button_pressed_a3(): + basic.show_icon(IconNames.SILLY) + basic.show_icon(IconNames.SURPRISED) +input.on_button_pressed(Button.AB, on_button_pressed_a3) +``` + +## {Step 5} + +Click ``|Download|`` to transfer your code to your @boardname@ (if you have one). Try buttons **A**, **B** and then **A** and **B** together. diff --git a/docs/projects/railway-crossing.md b/docs/projects/railway-crossing.md index afe13ec6c1b..faa7ea85a3c 100644 --- a/docs/projects/railway-crossing.md +++ b/docs/projects/railway-crossing.md @@ -15,7 +15,7 @@ We are going to use the light sensor to detect if a train is passing. We will do Let's first explore how the light sensor works by downloading the following program onto our @boardname@. ```block -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { basic.showNumber(input.lightLevel()) }) ``` @@ -49,7 +49,7 @@ Add the following blocks to your program to make the top-left led indicate if a Replace 40 with your threshold. ```block -basic.forever(() => { +basic.forever(function () { if (input.lightLevel() < 40) { led.plot(0, 0) } else { @@ -95,7 +95,7 @@ We can turn on one LED by writing a digital 1 to one pin and a digital 0 to the Now use the following program to make the lights blink indefinitely. ```block -basic.forever(() => { +basic.forever(function () { pins.digitalWritePin(DigitalPin.P1, 1) pins.digitalWritePin(DigitalPin.P2, 0) basic.pause(300) @@ -120,11 +120,11 @@ First of all, remove the forever block from step 5. Then add the following code: ```block let flashes_remaining = 0 -input.onButtonPressed(Button.B, () => { +input.onButtonPressed(Button.B, function () { flashes_remaining = 5 }) -basic.forever(() => { +basic.forever(function () { while (flashes_remaining > 0) { pins.digitalWritePin(DigitalPin.P1, 0) pins.digitalWritePin(DigitalPin.P2, 1) diff --git a/docs/projects/rc-car/code.md b/docs/projects/rc-car/code.md index 99688065431..c66ec8f616a 100644 --- a/docs/projects/rc-car/code.md +++ b/docs/projects/rc-car/code.md @@ -18,7 +18,7 @@ https://youtu.be/pD6tM1nXCPA The first program has the car drive around in a circle for 5 seconds when the user presses the ``A`` button. This is simply done by turning both motor controllers on for 5 seconds. ```blocks-ignore -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { basic.showIcon(IconNames.Happy) kitronik.motorOn(kitronik.Motors.Motor1, kitronik.MotorDirection.Reverse, 100) kitronik.motorOn(kitronik.Motors.Motor2, kitronik.MotorDirection.Forward, 100) @@ -46,7 +46,7 @@ https://youtu.be/agor9wtiAkE Instead of stopping after 5 seconds, we reverse the steering motor to turn in the other direction. This will create a figure eight path. ```blocks-ignore -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { basic.showIcon(IconNames.Happy) kitronik.motorOn(kitronik.Motors.Motor1, kitronik.MotorDirection.Reverse, 100) kitronik.motorOn(kitronik.Motors.Motor2, kitronik.MotorDirection.Forward, 100) diff --git a/docs/projects/rc-car/connect.md b/docs/projects/rc-car/connect.md index f020ebc4a6f..e659f82b97f 100644 --- a/docs/projects/rc-car/connect.md +++ b/docs/projects/rc-car/connect.md @@ -14,23 +14,23 @@ radio.onReceivedValue(function (name: string, value: number) { led.toggle(0, 0) if (name == "throttle") { if (value > 0) { - kitronik.motorOn(kitronik.Motors.Motor1, kitronik.MotorDirection.Reverse, 100); + kitronik.motorOn(kitronik.Motors.Motor1, kitronik.MotorDirection.Reverse, 100) } else if (value < 0) { - kitronik.motorOn(kitronik.Motors.Motor1, kitronik.MotorDirection.Forward, 100); + kitronik.motorOn(kitronik.Motors.Motor1, kitronik.MotorDirection.Forward, 100) } else { kitronik.motorOff(kitronik.Motors.Motor1); } } else if (name == "steering") { if (value > 0) { - kitronik.motorOn(kitronik.Motors.Motor2, kitronik.MotorDirection.Forward, 100); + kitronik.motorOn(kitronik.Motors.Motor2, kitronik.MotorDirection.Forward, 100) } else if (value < 0) { - kitronik.motorOn(kitronik.Motors.Motor2, kitronik.MotorDirection.Reverse, 100); + kitronik.motorOn(kitronik.Motors.Motor2, kitronik.MotorDirection.Reverse, 100) } else { - kitronik.motorOff(kitronik.Motors.Motor2); + kitronik.motorOff(kitronik.Motors.Motor2) } } }) -basic.forever(() => { +basic.forever(function () { throttle = 0 if (input.buttonIsPressed(Button.A)) { throttle = 100 diff --git a/docs/projects/reaction-time.md b/docs/projects/reaction-time.md index b773b6edb0e..e708b37bbb3 100644 --- a/docs/projects/reaction-time.md +++ b/docs/projects/reaction-time.md @@ -31,8 +31,4 @@ https://youtu.be/doHwknM7HbQ Let's get started! -## ~ - -## Flipgrid - -https://flipgrid.com/7120893b \ No newline at end of file +## ~ \ No newline at end of file diff --git a/docs/projects/reaction-time/code.md b/docs/projects/reaction-time/code.md index ec2d01be35d..5f804ad14fa 100644 --- a/docs/projects/reaction-time/code.md +++ b/docs/projects/reaction-time/code.md @@ -46,10 +46,10 @@ let start = 0 let end = 0 let false_start = false let running = false -input.onPinPressed(TouchPin.P0, () => { +input.onPinPressed(TouchPin.P0, function () { }) -input.onPinPressed(TouchPin.P1, () => { +input.onPinPressed(TouchPin.P1, function () { }) running = false @@ -67,13 +67,13 @@ let start = 0 let end = 0 let false_start = false let running = false -input.onPinPressed(TouchPin.P0, () => { +input.onPinPressed(TouchPin.P0, function () { basic.showNumber(3) basic.showNumber(2) basic.showNumber(1) basic.clearScreen() }) -input.onPinPressed(TouchPin.P1, () => { +input.onPinPressed(TouchPin.P1, function () { }) running = false @@ -93,15 +93,15 @@ let start = 0 let end = 0 let false_start = false let running = false -input.onPinPressed(TouchPin.P0, () => { +input.onPinPressed(TouchPin.P0, function () { + running = false + false_start = false basic.showNumber(3) basic.showNumber(2) basic.showNumber(1) basic.clearScreen() - running = false - false_start = false }) -input.onPinPressed(TouchPin.P1, () => { +input.onPinPressed(TouchPin.P1, function () { }) running = false @@ -119,16 +119,16 @@ let start = 0 let end = 0 let false_start = false let running = false -input.onPinPressed(TouchPin.P0, () => { +input.onPinPressed(TouchPin.P0, function () { + running = false + false_start = false basic.showNumber(3) basic.showNumber(2) basic.showNumber(1) basic.clearScreen() - running = false - false_start = false basic.pause(1000 + randint(0, 2000)) }) -input.onPinPressed(TouchPin.P1, () => { +input.onPinPressed(TouchPin.P1, function () { }) running = false @@ -146,16 +146,16 @@ let start = 0 let end = 0 let false_start = false let running = false -input.onPinPressed(TouchPin.P1, () => { +input.onPinPressed(TouchPin.P1, function () { }) -input.onPinPressed(TouchPin.P0, () => { +input.onPinPressed(TouchPin.P0, function () { + running = false + false_start = false basic.showNumber(3) basic.showNumber(2) basic.showNumber(1) basic.clearScreen() - running = false - false_start = false basic.pause(1000 + randint(0, 2000)) if (!(false_start)) { start = input.runningTime() @@ -184,7 +184,7 @@ let start = 0 let end = 0 let false_start = false let running = false -input.onPinPressed(TouchPin.P1, () => { +input.onPinPressed(TouchPin.P1, function () { if (running) { running = false end = input.runningTime() @@ -208,13 +208,13 @@ input.onPinPressed(TouchPin.P1, () => { `) } }) -input.onPinPressed(TouchPin.P0, () => { +input.onPinPressed(TouchPin.P0, function () { + running = false + false_start = false basic.showNumber(3) basic.showNumber(2) basic.showNumber(1) basic.clearScreen() - running = false - false_start = false basic.pause(1000 + randint(0, 2000)) if (!(false_start)) { start = input.runningTime() @@ -241,13 +241,13 @@ let start = 0 let end = 0 let false_start = false let running = false -input.onPinPressed(TouchPin.P0, () => { +input.onPinPressed(TouchPin.P0, function () { + running = false + false_start = false basic.showNumber(3) basic.showNumber(2) basic.showNumber(1) basic.clearScreen() - running = false - false_start = false basic.pause(1000 + randint(0, 2000)) if (!(false_start)) { start = input.runningTime() @@ -257,7 +257,7 @@ input.onPinPressed(TouchPin.P0, () => { led.plot(randint(0, 4), randint(0, 4)) } }) -input.onPinPressed(TouchPin.P1, () => { +input.onPinPressed(TouchPin.P1, function () { if (running) { running = false end = input.runningTime() @@ -281,7 +281,7 @@ input.onPinPressed(TouchPin.P1, () => { `) } }) -input.onPinPressed(TouchPin.P2, () => { +input.onPinPressed(TouchPin.P2, function () { if (running) { running = false end = input.runningTime() @@ -306,3 +306,91 @@ input.onPinPressed(TouchPin.P2, () => { } }) ``` + +## Extending the Extension + +One effect of the extension is the **X** for a false start shows for the person loosing the game. We can extend the code some more to avoid the **X** showing up on the person loosing the game. The following example uses a new variable to flag the winner and avoid the **X** after a winner is crowned. + +```blocks +input.onPinPressed(TouchPin.P0, function () { + running = false + false_start = false + Winner = 0 + basic.showNumber(3) + basic.showNumber(2) + basic.showNumber(1) + basic.clearScreen() + basic.pause(1000 + randint(0, 2000)) + if (!(false_start)) { + start = input.runningTime() + running = true + led.stopAnimation() + basic.clearScreen() + led.plotBrightness(randint(0, 4), randint(0, 4), 255) + } +}) +input.onPinPressed(TouchPin.P2, function () { + if (running) { + running = false + end = input.runningTime() + Winner = 2 + basic.showLeds(` + . . . # # + . . . # # + . . . # # + . . . # # + . . . # # + `) + basic.pause(1000) + basic.showNumber(end - start) + } else if (Winner == 1) { + + } else { + false_start = true + basic.showLeds(` + . . . . . + . . # . # + . . . # . + . . # . # + . . . . . + `) + } +}) +input.onPinPressed(TouchPin.P1, function () { + if (running) { + running = false + end = input.runningTime() + Winner = 1 + basic.showLeds(` + # # . . . + # # . . . + # # . . . + # # . . . + # # . . . + `) + basic.pause(1000) + basic.showNumber(end - start) + } else if (Winner == 2) { + + } else { + false_start = true + basic.showLeds(` + . . . . . + # . # . . + . # . . . + # . # . . + . . . . . + `) + } +}) +let Winner = 0 +let start = 0 +let end = 0 +let false_start = false +let running = false +running = false +false_start = false +end = 0 +start = 0 +Winner = 0 +``` diff --git a/docs/projects/red-light-green-light.md b/docs/projects/red-light-green-light.md index 21e71c91af8..490ad2dc59f 100644 --- a/docs/projects/red-light-green-light.md +++ b/docs/projects/red-light-green-light.md @@ -203,7 +203,7 @@ basic.forever(function () { basic.forever(function () { if (state == REDLIGHT) { movement = Math.abs(input.acceleration(Dimension.Strength) - 1000) - if (movement != 0) { + if (movement > 100) { game.gameOver() } } diff --git a/docs/projects/rock-paper-scissors-v2.md b/docs/projects/rock-paper-scissors-v2.md new file mode 100644 index 00000000000..f4404587d40 --- /dev/null +++ b/docs/projects/rock-paper-scissors-v2.md @@ -0,0 +1,205 @@ +# Rock Paper Scissors V2 + +## {Introduction @unplugged} + +![Cartoon of the Rock Paper Scissors game](/static/mb/projects/a4-motion-v2.png) + +Build a "Rock Paper Scissors" game with ADDED BONUS SOUNDS using the **micro:bit V2** buzzer! + +## {Step 1 @fullscreen} + +Use the ``||input:on shake||`` block in the Workspace to run code when you shake the @boardname@. + +```blocks +input.onGesture(Gesture.Shake, function () { + +}) +``` + +## {Step 2 @fullscreen} + +Make a new variable called ``hand`` and place the ``||variables:set hand to||`` block in the shake event. + +![A animation that shows how to create a variable](/static/mb/projects/rock-paper-scissors/newvar.gif) + +## {Step 3 @fullscreen} + +Add a ``||math:pick random||`` block to pick a random number from `1` to `3` and store it in the variable named ``hand``. + +```blocks +let hand = 0; +input.onGesture(Gesture.Shake, function () { + hand = randint(1, 3) +}) +``` + +In a later step, each of the possible numbers (`1`, `2`, or `3`) is matched to its own picture. The picture is shown on the LEDs when its matching number is picked. + +## {Step 4 @fullscreen} + +Place an ``||logic:if||`` block under the ``||math:pick random||`` and check whether ``hand`` is equal to ``1``. Add a ``||basic:show leds||`` block that shows a picture of a piece of paper. The number `1` is the value for paper. + +![How to drag an if statement](/static/mb/projects/rock-paper-scissors/if.gif) + +```blocks +let hand = 0; +input.onGesture(Gesture.Shake, function () { + hand = randint(1, 3) + if (hand == 1) { + basic.showLeds(` + # # # # # + # . . . # + # . . . # + # . . . # + # # # # # + `) + } +}) +``` + +## {Step 5 @fullscreen} + +Place a ``||music:play sound||`` block under ``||basic:show leds||`` and edit it to make it sound like paper. + +```blocks +let hand = 0; +input.onGesture(Gesture.Shake, function () { + hand = randint(1, 3) + if (hand == 1) { + basic.showLeds(` + # # # # # + # . . . # + # . . . # + # . . . # + # # # # # + `) + music.playSoundEffect(music.createSoundEffect(WaveShape.Noise, 4120, 1266, 255, 148, 500, SoundExpressionEffect.Warble, InterpolationCurve.Curve), SoundExpressionPlayMode.UntilDone) + } +}) +``` + +## {Step 6 @fullscreen} + +Click on the **SHAKE** button in the simulator. If you try enough times, you should see a picture of paper on the screen. + +![Shaking a @boardname@ simulator](/static/mb/projects/rock-paper-scissors/rpsshake.gif) + + +## {Step 7 @fullscreen} + +Click the **(+)** button to add an ``||logic:else||`` section. + +![Adding an else clause](/static/mb/projects/rock-paper-scissors/ifelse.gif) + +```blocks +let hand = 0; +input.onGesture(Gesture.Shake, function () { + hand = randint(1, 3) + if (hand == 1) { + basic.showLeds(` + # # # # # + # . . . # + # . . . # + # . . . # + # # # # # + `) + music.playSoundEffect(music.createSoundEffect(WaveShape.Noise, 4120, 1266, 255, 148, 500, SoundExpressionEffect.Warble, InterpolationCurve.Curve), SoundExpressionPlayMode.UntilDone) + } else { + + } +}) +``` + +## {Step 8 @fullscreen} + +Add both a ``||basic:show leds||`` block and a ``||music:play sound||`` block inside the ``||logic:else||``. Make a picture for **scissors** using LEDs and create a scissors sound. + +```blocks +let hand = 0; +input.onGesture(Gesture.Shake, function () { + hand = randint(1, 3) + if (hand == 1) { + basic.showLeds(` + # # # # # + # . . . # + # . . . # + # . . . # + # # # # # + `) + music.playSoundEffect(music.createSoundEffect(WaveShape.Noise, 4120, 1266, 255, 148, 500, SoundExpressionEffect.Warble, InterpolationCurve.Curve), SoundExpressionPlayMode.UntilDone) + } else { + basic.showLeds(` + # # . . # + # # . # . + . . # . . + # # . # . + # # . . # + `) + music.playSoundEffect(music.createSoundEffect(WaveShape.Sine, 4417, 1, 0, 255, 266, SoundExpressionEffect.Vibrato, InterpolationCurve.Linear), SoundExpressionPlayMode.UntilDone) + } +}) +``` + +## {Step 9 @fullscreen} + +Click the **(+)** button again to add an ``||logic:else if||`` section. Now, add a conditional block for ``||logic:hand = 2||`` to the empty slot in the ``||logic:else if||``. Since ``hand`` can only be `1`, `2`, or `3`, your code is now covering all possible cases! + +![Adding an else if clause](/static/mb/projects/rock-paper-scissors/ifelseif.gif) + +## {Step 10 @fullscreen} + +Get one more ``||basic:show leds||`` block and ``||music:play sound||`` block and put them inside the ``||logic:else if||``. Make a picture of a rock in the LEDs and create a rock-like sound. + +```blocks +let hand = 0 +input.onGesture(Gesture.Shake, function () { + hand = randint(1, 3) + if (hand == 1) { + basic.showLeds(` + # # # # # + # . . . # + # . . . # + # . . . # + # # # # # + `) + music.playSoundEffect(music.createSoundEffect(WaveShape.Noise, 4120, 1266, 255, 148, 500, SoundExpressionEffect.Warble, InterpolationCurve.Curve), SoundExpressionPlayMode.UntilDone) + } else if (hand == 2) { + basic.showLeds(` + . . . . . + . # # # . + . # # # . + . # # # . + . . . . . + `) + music.playSoundEffect(music.createSoundEffect(WaveShape.Sine, 4417, 1, 0, 255, 266, SoundExpressionEffect.Vibrato, InterpolationCurve.Linear), SoundExpressionPlayMode.UntilDone) + } else { + basic.showLeds(` + # # . . # + # # . # . + . . # . . + # # . # . + # # . . # + `) + music.playSoundEffect(music.createSoundEffect(WaveShape.Triangle, 1177, 4967, 0, 206, 266, SoundExpressionEffect.Tremolo, InterpolationCurve.Linear), SoundExpressionPlayMode.UntilDone) + } +}) +``` + +## {Step 11 @fullscreen} + +Click on the **SHAKE** button in the simulator and check to see that each image is showing up. + +![Shaking a @boardname@ simulator](/static/mb/projects/rock-paper-scissors/rpssim3.gif) + +## {Step 12 @fullscreen} + +If you have a @boardname@ V2, click on ``|Download|`` and follow the instructions to get the code +onto your @boardname@. + +Your game is ready! Gather your friends and play Rock Paper Scissors! + +![A @boardname@ in a hand](/static/mb/projects/rock-paper-scissors/hand.jpg) + +```template +input.onGesture(Gesture.Shake, function() {}) +``` diff --git a/docs/projects/rock-paper-scissors.md b/docs/projects/rock-paper-scissors.md index 352a20b661e..431cf7790fe 100644 --- a/docs/projects/rock-paper-scissors.md +++ b/docs/projects/rock-paper-scissors.md @@ -1,171 +1,230 @@ # Rock Paper Scissors -## Introduction @unplugged +## {Introduction @unplugged} ![Cartoon of the Rock Paper Scissors game](/static/mb/projects/a4-motion.png) -Use the accelerometer and the screen to build a **Rock Paper Scissors** game that you can play with your friends! +Turn your micro:bit into a **Rock Paper Scissors** game that you can play with your friends! -## Step 1 @fullscreen +## {Step 1} -Add a ``||input:on shake||`` block to run code when you shake the @boardname@. +First we need to make a variable to keep track of whether we have a Rock, Paper or Scissors in our hand. A variable is a container for storing values. Click on the ``||variables:Variables||`` category in the Toolbox. Click on the **Make a Variable** button. Give your new variable the name "hand" and click Ok. -```blocks -input.onGesture(Gesture.Shake, () => { +![A animation that shows how to create a variable](/static/mb/projects/rock-paper-scissors/newvar.gif) + +## {Step 2} +Click on the ``||variables:Variables||`` category in the Toolbox again. You'll notice that there are some new blocks that have appeared. Drag a ``||variables:set hand||`` block into the ``||input:on shake||`` block. We'll start our Rock Paper Scissors game when we shake 👋 our micro:bit. + +```blocks +let hand = 0; +input.onGesture(Gesture.Shake, function() { + hand = 0 }) ``` -## Step 2 @fullscreen +## {Step 3} -Add a ``hand`` variable and place the ``||variables:set hand to||`` block in the shake event. +Click on the ``||math:Math||`` category in the Toolbox. Drag a ``||math:pick random||`` block and drop it into the ``||variables:set hand||`` block replacing the number 0. Now when we shake our micro:bit, the variable hand will contain a random number between 1 and 3. -![A animation that shows how to create a variable](/static/mb/projects/rock-paper-scissors/newvar.gif) +```blocks +let hand = 0; +input.onGesture(Gesture.Shake, function() { + hand = randint(1, 3) +}) +``` -## Step 3 @fullscreen +## {Step 4} -Add a ``||math:pick random||`` block to pick a random number from `1` to `3` and store it in the variable named ``hand``. +Click on the ``||logic:Logic||`` category in the Toolbox. Drag the ``||logic:if true then else||`` block out to the workspace and drop it into the ``||input:on shake||`` block under the ``||variables:set hand||`` block. ```blocks let hand = 0; -input.onGesture(Gesture.Shake, () => { +input.onGesture(Gesture.Shake, function() { hand = randint(1, 3) + if (true) { + + } else { + + } }) ``` -In a later step, each of the possible numbers (`1`, `2`, or `3`) is matched to its own picture. The picture is shown on the LEDs when its matching number is picked. +## {Step 5} + +From the ``||logic:Logic||`` category, drag a ``||logic:0 = 0||`` comparison block and drop it into the ``||logic:if true then else||`` block replacing **true**. -## Step 4 @fullscreen +```blocks +let hand = 0; +input.onGesture(Gesture.Shake, function() { + hand = randint(1, 3) + if (0 == 0) { + + } else { + + } +}) +``` -Place an ``||logic:if||`` block under the ``||math:pick random||`` and check whether ``hand`` is equal to ``1``. Add a ``||basic:show leds||`` block that shows a picture of a piece of paper. The number `1` will mean paper. +## {Step 6} -![How to drag an if statement](/static/mb/projects/rock-paper-scissors/if.gif) +Click on the ``||variables:Variables||`` category in the Toolbox. Drag a ``||variables:hand||`` block out and drop it into the ``||logic:0 = 0||`` comparison block replacing the first **0**. Click on the second 0 in the comparison block and change to **1**. ```blocks let hand = 0; -input.onGesture(Gesture.Shake, () => { +input.onGesture(Gesture.Shake, function() { hand = randint(1, 3) if (hand == 1) { - basic.showLeds(` - # # # # # - # . . . # - # . . . # - # . . . # - # # # # # - `) + + } else { + } }) ``` -## Step 5 @fullscreen - -Click on the **SHAKE** button in the simulator. If you try enough times, you should see a picture of paper on the screen. +## {Step 7} -![Shaking a @boardname@ simulator](/static/mb/projects/rock-paper-scissors/rpsshake.gif) +Click on the ``||basic:Basic||`` category in the Toolbox. Drag a ``||basic:show icon||`` block out and drop it under ``||logic:if hand = 1 then||``. In the ``||basic:show icon||`` block, click on the Heart icon and instead select the small square icon to represent a 💎 Rock. -## Step 6 @fullscreen +```blocks +let hand = 0; +input.onGesture(Gesture.Shake, function() { + hand = randint(1, 3) + if (hand == 1) { + basic.showIcon(IconNames.SmallSquare) + } else { + + } +}) +``` -Click the **(+)** button to add an ``||logic:else||`` section. +## {Step 8} -![Adding an else clause](/static/mb/projects/rock-paper-scissors/ifelse.gif) +At the bottom of the ``||logic:if then else||`` block, click on the plus **'+'** sign. This will expand the code to include an ``||logic:else if||`` clause. ```blocks let hand = 0; -input.onGesture(Gesture.Shake, () => { +input.onGesture(Gesture.Shake, function() { hand = randint(1, 3) if (hand == 1) { - basic.showLeds(` - # # # # # - # . . . # - # . . . # - # . . . # - # # # # # - `) + basic.showIcon(IconNames.SmallSquare) + } else if (false) { + } else { + + } +}) +``` + +## {Step 9} + +From the ``||logic:Logic||`` category, drag a ``||logic:0 = 0||`` comparison block and drop it into the open space next to the ``||logic:else if||`` clause. +```blocks +let hand = 0; +input.onGesture(Gesture.Shake, function() { + hand = randint(1, 3) + if (hand == 1) { + basic.showIcon(IconNames.SmallSquare) + } else if (0 == 0) { + + } else { + } }) ``` -## Step 7 @fullscreen +## {Step 10} -Add a ``||basic:show leds||`` block inside the ``||logic:else||``. Make a picture of a scissors in the LEDs. +From the ``||variables:Variables||`` category, drag a ``||variables:hand||`` block and drop it into the ``||logic:0 = 0||`` comparison block replacing the first **0**. Click on the second 0 in the comparison block and change to **2**. ```blocks let hand = 0; -input.onGesture(Gesture.Shake, () => { +input.onGesture(Gesture.Shake, function() { hand = randint(1, 3) if (hand == 1) { - basic.showLeds(` - # # # # # - # . . . # - # . . . # - # . . . # - # # # # # - `) + basic.showIcon(IconNames.SmallSquare) + } else if (hand == 2) { + } else { - basic.showLeds(` - # # . . # - # # . # . - . . # . . - # # . # . - # # . . # - `) + } }) ``` -## Step 8 @fullscreen +## {Step 11} -Click the ``+`` button again to add an ``||logic:else if||`` section. Now, add a conditional block for ``||logic:hand = 2||`` to the condition in ``||logic:else if||``. Since ``hand`` can only be `1`, `2`, or `3`, your code is covering all possible cases! +From the ``||basic:Basic||`` category, drag a ``||basic:show icon||`` block out and drop it under ``||logic:else if hand = 2 then||``. In the ``||basic:show icon||`` block, click on the Heart icon and instead select the large square icon to represent 📃 Paper. -![Adding an else if clause](/static/mb/projects/rock-paper-scissors/ifelseif.gif) +```blocks +let hand = 0; +input.onGesture(Gesture.Shake, function() { + hand = randint(1, 3) + if (hand == 1) { + basic.showIcon(IconNames.SmallSquare) + } else if (hand == 2) { + basic.showIcon(IconNames.Square) + } else { + + } +}) +``` -## Step 9 @fullscreen +## {Step 12} -Get one more ``||basic:show leds||`` block and put it in the ``||logic:else if||``. Make a picture of a rock in the LEDs. +Now let's deal with the last condition - if our hand variable isn't holding a 1 (Rock) or a 2 (Paper), then it must be 3 (âœ‚ī¸ Scissors)! From the ``||basic:Basic||`` category, drag another ``||basic:show icon||`` block out and drop it into the last opening under the ``||logic:else||``. In the ``||basic:show icon||`` block, click on the Heart icon and select the Scissors icon. ```blocks let hand = 0; -input.onGesture(Gesture.Shake, () => { +input.onGesture(Gesture.Shake, function() { hand = randint(1, 3) if (hand == 1) { - basic.showLeds(` - # # # # # - # . . . # - # . . . # - # . . . # - # # # # # - `) + basic.showIcon(IconNames.SmallSquare) } else if (hand == 2) { - basic.showLeds(` - . . . . . - . # # # . - . # # # . - . # # # . - . . . . . - `) + basic.showIcon(IconNames.Square) } else { - basic.showLeds(` - # # . . # - # # . # . - . . # . . - # # . # . - # # . . # - `) + basic.showIcon(IconNames.Scissors) } }) ``` -## Step 10 @fullscreen +## {Step 13} -Click on the **SHAKE** button in the simulator and check to see that each image is showing up. +Let's test your code! Press the white **SHAKE** button on the micro:bit on-screen simulator, or move your cursor quickly back and forth over the simulator. Do you see the icons for rock, paper and scissors randomly appear? ⭐ Great job! ⭐ ![Shaking a @boardname@ simulator](/static/mb/projects/rock-paper-scissors/rpssim3.gif) -## Step 11 @fullscreen +## {Step 14} -If you have a @boardname@, click on ``|Download|`` and follow the instructions to get the code -onto your @boardname@. Your game is ready! Gather your friends and play Rock Paper Scissors! +If you have a @boardname@ device, connect it to your computer and click the ``|Download|`` button. Follow the instructions to transfer your code onto the @boardname@. Once your code has been downloaded, attach your micro:bit to a battery pack and challenge another micro:bit or a human to a game of Rock, Paper, Scissors! ![A @boardname@ in a hand](/static/mb/projects/rock-paper-scissors/hand.jpg) + +## {Step 15} + +Go further - Try adding đŸŽĩ Music đŸŽĩ blocks to your Rock Paper Scissors game for different sound effects. Note that some Music blocks may require a micro:bit v2 device to play. + +```blocks +let hand = 0 +input.onGesture(Gesture.Shake, function () { + hand = randint(1, 3) + if (hand == 1) { + basic.showIcon(IconNames.SmallSquare) + music.play(music.builtinPlayableSoundEffect(soundExpression.giggle), music.PlaybackMode.UntilDone) + } else if (hand == 2) { + basic.showIcon(IconNames.Square) + music.play(music.tonePlayable(262, music.beat(BeatFraction.Whole)), music.PlaybackMode.UntilDone) + } else { + basic.showIcon(IconNames.Scissors) + music.play(music.createSoundExpression(WaveShape.Square, 1600, 1, 255, 0, 300, SoundExpressionEffect.None, InterpolationCurve.Curve), music.PlaybackMode.UntilDone) + } +}) +``` + +```blockconfig.global +randint(1, 3) +``` + +```template +input.onGesture(Gesture.Shake, function() {}) +``` diff --git a/docs/projects/salute.md b/docs/projects/salute.md index 770efd986b8..3f3fbfeb00c 100644 --- a/docs/projects/salute.md +++ b/docs/projects/salute.md @@ -37,7 +37,7 @@ Choose a random number between 0 and 9. ```blocks let randomNbr = 0 -input.onGesture(Gesture.ScreenUp, () => { +input.onGesture(Gesture.ScreenUp, function () { randomNbr = randint(0, 10) basic.showNumber(randomNbr) }) @@ -47,7 +47,7 @@ Choose a random number between 1 and 9. ```blocks let randomNbr = 0 -input.onGesture(Gesture.ScreenUp, () => { +input.onGesture(Gesture.ScreenUp, function () { randomNbr = 0 while (randomNbr < 1) { randomNbr = randint(0, 10) @@ -63,13 +63,13 @@ The score keeper program adds one point for a player when button ``A`` or ``B`` ```blocks let player1Score = 0 let player2Score = 0 -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { player1Score += 1 }) -input.onButtonPressed(Button.B, () => { +input.onButtonPressed(Button.B, function () { player2Score += 1 }) -input.onButtonPressed(Button.AB, () => { +input.onButtonPressed(Button.AB, function () { if (player1Score == player2Score) { basic.showString("TIE") } else if (player1Score > player2Score) { diff --git a/docs/projects/servo-calibrator.md b/docs/projects/servo-calibrator.md index 14a8dc89844..582ce139852 100644 --- a/docs/projects/servo-calibrator.md +++ b/docs/projects/servo-calibrator.md @@ -9,17 +9,17 @@ in a loop. ```blocks let angle = 90 -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { angle = Math.max(0, angle - 5) pins.servoWritePin(AnalogPin.P0, angle) led.stopAnimation() }) -input.onButtonPressed(Button.B, () => { +input.onButtonPressed(Button.B, function () { angle = Math.min(180, angle + 5) pins.servoWritePin(AnalogPin.P0, angle) led.stopAnimation() }) -basic.forever(() => { +basic.forever(function () { basic.showNumber(angle) }) pins.servoWritePin(AnalogPin.P0, angle) @@ -27,4 +27,4 @@ pins.servoWritePin(AnalogPin.P0, angle) ## See also -[Brief Guide to Servos](https://www.kitronik.co.uk/pdf/a-brief-guide-to-servos.pdf) +[Brief Guide to Servos](https://kitronik.co.uk/blogs/resources/servos-brief-guide) diff --git a/docs/projects/smiley-buttons.md b/docs/projects/smiley-buttons.md index 512d3e9ed79..232c1a229d1 100644 --- a/docs/projects/smiley-buttons.md +++ b/docs/projects/smiley-buttons.md @@ -1,81 +1,62 @@ # Smiley Buttons -## Introduction @unplugged +## Code a micro:bit emoji! @unplugged -Code the buttons on the @boardname@ to show that it's happy or sad. -(Want to learn how the buttons works? [Watch this video](https://youtu.be/t_Qujjd_38o)). +Program the buttons on the @boardname@ to show a happy 😀 or sad face 🙁 ![Pressing the A and B buttons](/static/mb/projects/smiley-buttons/sim.gif) -## Step 1 +## {Step 1} -Place a ``||input:on button pressed||`` block to run code when button **A** is pressed. +Let's show a happy face when we press button **A**. +Click on the ``||basic:Basic||`` category in the Toolbox. Drag a ``||basic:show icon||`` block into the ``||input:on button A pressed||`` block. +In the ``||basic:show icon||`` block, click on the Heart icon to open the menu. Select a Happy Face icon. ```blocks -input.onButtonPressed(Button.A, () => { -}); +input.onButtonPressed(Button.A, function() { + basic.showIcon(IconNames.Happy) +}) ``` -## Step 2 +## {Step 2} -Place a ``||basic:show leds||`` block inside ``||input:on button pressed||`` to display a smiley on the screen. Press the **A** button in the simulator to see the smiley. +In the @boardname@ simulator on the screen, press the **A** button. Do you see a happy face? ⭐ Great job! ⭐ -```blocks -input.onButtonPressed(Button.A, () => { - basic.showLeds(` - # # . # # - # # . # # - . . . . . - # . . . # - . # # # .` - ); -}); -``` +## {Step 3} -## Step 3 - -Add ``||input:on button pressed||`` and ``||basic:show leds||`` blocks to display a frowny when button **B** is pressed. +Now let's show a sad face when we press button **B**. +Click on the ``||input:Input||`` category in the Toolbox. +Drag another ``||input:on button A pressed||`` block onto the coding workspace (you can place this anywhere). +Click on the **A** button drop-down menu, and select **B**. ```blocks -input.onButtonPressed(Button.B, () => { - basic.showLeds(` - # # . # # - # # . # # - . . . . . - . # # # . - # . . . #` - ); -}); +input.onButtonPressed(Button.B, function() {}) ``` -## Step 4 +## {Step 4} -Add a secret mode that happens when **A** and **B** are pressed together. For this case, add multiple ``||basic:show leds||`` blocks to create an animation. +From the ``||basic:Basic||`` category, drag another ``||basic:show icon||`` block into the ``||input:on button B pressed||`` block. +In this ``||basic:show icon||`` block, click on the Heart icon to open the menu. +Select a Sad Face icon. ```blocks -input.onButtonPressed(Button.AB, () => { - basic.showLeds(` - . . . . . - # . # . . - . . . . . - # . . . # - . # # # . - `) - basic.showLeds(` - . . . . . - . . # . # - . . . . . - # . . . # - . # # # . - `) +input.onButtonPressed(Button.B, function() { + basic.showIcon(IconNames.Sad) }) ``` +## {Step 5} + +In the @boardname@ simulator on the screen, press the **B** button. Do you see a sad face? ⭐ Great job! ⭐ -## Step 5 +## {Step 6} -If you have a @boardname@, connect it to USB and click ``|Download|`` to transfer your code. Press button **A** on your @boardname@. Try button **B** and then **A** and **B** together. +If you have a @boardname@ device, connect it to your computer and click the ``|Download|`` button. Follow the instructions to transfer your code onto the @boardname@. Try pressing the **A** and **B** buttons on the micro:bit to see your happy 😀 and sad 🙁 emojis! -## Step 6 +## {Step 7} -Nice! Now go and show it off to your friends! +Go further - try adding a secret emoji that appears when **A** and **B** buttons are pressed together! +Learn more about how the @boardname@ buttons work by watching [this video](https://youtu.be/t_Qujjd_38o). +```template +input.onButtonPressed(Button.A, function() {}) +``` \ No newline at end of file diff --git a/docs/projects/snap-the-dot.md b/docs/projects/snap-the-dot.md index 0055b8a0b64..1b3fbb08ae0 100644 --- a/docs/projects/snap-the-dot.md +++ b/docs/projects/snap-the-dot.md @@ -64,8 +64,8 @@ input.onButtonPressed(Button.A, function () { }) basic.forever(function () { sprite.move(1) - basic.pause(100) sprite.ifOnEdgeBounce() + basic.pause(100) }) ``` @@ -84,8 +84,8 @@ input.onButtonPressed(Button.A, function () { }) basic.forever(function () { sprite.move(1) - basic.pause(100) sprite.ifOnEdgeBounce() + basic.pause(100) }) ``` diff --git a/docs/projects/soil-moisture/code.md b/docs/projects/soil-moisture/code.md index d2872fd79c8..e331fd20977 100644 --- a/docs/projects/soil-moisture/code.md +++ b/docs/projects/soil-moisture/code.md @@ -12,7 +12,7 @@ To measure this, we read the voltage on pin **P0** using ``||pins:analog read pi which returns a value between ``0`` (no current) and ``1023`` (maximum current). The value is graph on the screen using ``||led:plot bar graph||``. ```blocks -basic.forever(() => { +basic.forever(function () { led.plotBarGraph( pins.analogReadPin(AnalogPin.P0), 1023 @@ -33,7 +33,7 @@ This code needs to go into the ``||basic:forever||`` loop. We've also added the ```blocks let reading = 0 -basic.forever(() => { +basic.forever(function () { reading = pins.analogReadPin(AnalogPin.P0) led.plotBarGraph( reading, @@ -47,8 +47,17 @@ basic.forever(() => { ### Experiment! -* Insert the nails in the dry dirt, press **A** and note the value. You should see a value close to around ``250``for dry dirt. -* Insert the nails in the wet dirt, press **A** and note the value. You should see a value somewhere near ``1000`` for wet dirt. +Test and record the **P0** input values for both very dry dirt and for dirt that is wet. This will let you know the what the moisture scale of your meter is. The dry soil will have a low value and the wet soil will have a higher value. + +1. Insert the nails in the dry dirt, press **A** and record the value. +2. Insert the nails in the wet dirt, press **A** and record the value. + +Here's an example test table of results for very dry and wet dirt using both versions of the @boardname@: + +| Soil | micro:bit V1 | micro:bit V2 | +|---|---|---| +| Dry | 250 | 600 | +| Wet | 1000 | 1000 |
https://youtu.be/S8NppVT_paw @@ -68,10 +77,11 @@ This saves electricity and also avoids corrosion of the probes. ```blocks led.setBrightness(64) let reading = 0 -basic.forever(() => { - pins.analogWritePin(AnalogPin.P1, 1023) +basic.forever(function () { + pins.digitalWritePin(DigitalPin.P1, 1) + basic.pause(1) reading = pins.analogReadPin(AnalogPin.P0) - pins.analogWritePin(AnalogPin.P1, 0) + pins.digitalWritePin(DigitalPin.P1, 0) led.plotBarGraph( reading, 1023 diff --git a/docs/projects/soil-moisture/connect.md b/docs/projects/soil-moisture/connect.md index c75d653ab07..2c1f44bba41 100644 --- a/docs/projects/soil-moisture/connect.md +++ b/docs/projects/soil-moisture/connect.md @@ -9,15 +9,13 @@ To make it happen, we need to change the program to: * send the moisture level **divided by 4** as the dashboard takes values between ``0`` and ``255``. ```blocks -radio.setTransmitSerialNumber(true) -radio.setGroup(4) led.setBrightness(64) let reading = 0 -basic.forever(() => { - pins.analogWritePin(AnalogPin.P1, 1023) +basic.forever(function () { + pins.digitalWritePin(DigitalPin.P1, 1) + basic.pause(1) reading = pins.analogReadPin(AnalogPin.P0) - radio.sendNumber(reading / 4); - pins.analogWritePin(AnalogPin.P1, 0) + pins.digitalWritePin(DigitalPin.P1, 0) led.plotBarGraph( reading, 1023 @@ -25,7 +23,7 @@ basic.forever(() => { if (input.buttonIsPressed(Button.A)) { basic.showNumber(reading) } - basic.pause(5000); + basic.pause(5000) }) ``` diff --git a/docs/projects/spy/7-seconds.md b/docs/projects/spy/7-seconds.md index 3fe3f12fb44..89030c191d8 100644 --- a/docs/projects/spy/7-seconds.md +++ b/docs/projects/spy/7-seconds.md @@ -2,15 +2,15 @@ ### @explicitHints true -## Introduction @unplugged +## Can you react at the right time? @unplugged The goal of this game is press a button after **exactly** 7 seconds! ![A micro:bit looking at a 7 second stopwatch](/static/mb/projects/7-seconds.png) -This game is inspired from the [flipping panckakes game](https://www.elecfreaks.com/store/blog/post/flipping-pancakes-microbit-game.html). +This game is inspired from the [flipping pancakes game](https://www.elecfreaks.com/blog/post/flipping-pancakes-microbit-game.html). -## Step 1 +## {Step 1} The player starts the timer by pressing button **A**. Add the code to run code when ``||input:button A is pressed||``. @@ -21,7 +21,7 @@ input.onButtonPressed(Button.A, function () { }) ``` -## Step 2 +## {Step 2} We need to remember the time when the button was pressed so that we can compute the elapsed time later on. Add code to store the ``||input:running time||`` in a ``||variables:start||`` @@ -35,7 +35,7 @@ input.onButtonPressed(Button.A, function () { }) ``` -## Step 3 +## {Step 3} Show something on the screen so that the user knows that the timer has started... @@ -48,7 +48,7 @@ input.onButtonPressed(Button.A, function () { }) ``` -## Step 4 +## {Step 4} The player stops the timer by pressing button **B**. Add another event to run code when ``||input:button B is pressed||``. @@ -59,7 +59,7 @@ input.onButtonPressed(Button.B, function () { }) ``` -## Step 5 +## {Step 5} Compute the elapsed time as ``||input:running time||`` ``||math:minus||`` ``||variables:start||`` and store it in a new local variable (a variable only inside the event) called ``||variables:elapsed||``. @@ -72,7 +72,7 @@ input.onButtonPressed(Button.B, function () { }) ``` -## Step 6 +## {Step 6} Compute the ``||variables:score||`` of the game as the ``||math:absolute value||`` of the ``||math:difference||`` of ``||variables:elapsed||`` time from 7 seconds, which is `7000` @@ -87,7 +87,7 @@ input.onButtonPressed(Button.B, function () { }) ``` -## Step 7 +## {Step 7} Display the score on the screen and your game is ready! diff --git a/docs/projects/spy/coin-flipper.md b/docs/projects/spy/coin-flipper.md index 024d7a23d54..67471000a7d 100644 --- a/docs/projects/spy/coin-flipper.md +++ b/docs/projects/spy/coin-flipper.md @@ -2,23 +2,23 @@ ### @explicitHints true -## Introduction @unplugged +## Heads or Tails? @unplugged Let's create a coin flipping program to simulate a real coin toss. We'll use icon images to represent a ``heads`` or ``tails`` result. ![Simulating coin toss](/static/mb/projects/coin-flipper/coin-flipper.gif) -## Step 1 +## {Step 1} Add an event to run code when ``||input:button A pressed||``. We'll put our coin flipping code in here. ```spy -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { }) ``` -## Step 2 +## {Step 2} Inside the event for ``||input:button A pressed||``, put in code to check ``||logic:if||`` a ``||math:random boolean||`` value is `true` or `false`. @@ -26,20 +26,20 @@ The ``||math:random boolean||`` value is used to determine a ``heads`` or ``tail a coin toss. ```spy -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { if (Math.randomBoolean()) { } else { } }) ``` -## Step 3 +## {Step 3} -Now, ``||basic:show an icon||`` for a `skull` ``||logic:if||`` the ``||math:random boolean||`` value is ``true``. This means ``heads``. ``||basic:show and icon||`` of a ``square`` when ``false`` to mean +Now, ``||basic:show icon||`` for a `skull` ``||logic:if||`` the ``||math:random boolean||`` value is ``true``. This means ``heads``. ``||basic:show icon||`` of a ``square`` when ``false`` to mean ``tails``. ```spy -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { if (Math.randomBoolean()) { basic.showIcon(IconNames.Skull) } else { @@ -48,18 +48,18 @@ input.onButtonPressed(Button.A, () => { }) ``` -## Step 4 +## {Step 4} Press button **A** in the simulator to try the coin toss code. -## Step 5 +## {Step 5} You can animate the coin toss to add the feeling of suspense. ``||basic:show||`` different icons before the check of the ``||math:random boolean||`` value to show that the coin is flipping. ```spy -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { basic.showIcon(IconNames.Diamond) basic.showIcon(IconNames.SmallDiamond) basic.showIcon(IconNames.Diamond) @@ -72,10 +72,10 @@ input.onButtonPressed(Button.A, () => { }) ``` -## Step 6 +## {Step 6} If you have a @boardname@, connect it to USB and click ``|Download|`` to transfer your code. -## Step 7 +## {Step 7} Press button **A** for a flip. Test your luck and guess ``heads`` or ``tails`` before the toss is over! diff --git a/docs/projects/spy/compass.md b/docs/projects/spy/compass.md index e253fddd8bd..6749a7cb8c7 100644 --- a/docs/projects/spy/compass.md +++ b/docs/projects/spy/compass.md @@ -1,12 +1,12 @@ # Compass -## Introduction @unplugged +## Find your direction! @unplugged This tutorial will show you how to program a script that displays which direction the @boardname@ is pointing. Let's get started! ![A cartoon of a compass](/static/mb/projects/a5-compass.png) -## Step 1 +## {Step 1} Store the ``||input:compass heading||`` of the @boardname@ in a variable called ``||variables:degrees||`` in the ``||basic:forever||`` loop. @@ -16,7 +16,7 @@ basic.forever(function() { }) ``` -## Step 2 +## {Step 2} ``||logic:If||`` ``||variables:degrees||`` is ``||logic:less than||`` `45`, then the compass heading is mostly pointing toward **North**. ``||basic:Show||`` `N` on the @boardname@. @@ -30,7 +30,7 @@ basic.forever(function() { }) ``` -## Step 3 +## {Step 3} ``||logic:If||`` ``||variables:degrees||`` is less than `135`, the @boardname@ is mostly pointing **East**. ``||basic:Show||`` `E` on the @boardname@. @@ -46,11 +46,11 @@ basic.forever(function() { }) ``` -## Step 4 +## {Step 4} Go to the simulator and rotate the @boardname@ logo to simulate changes in the compass heading. -## Step 5 +## {Step 5} ``||logic:If||`` ``||variables:degrees||`` is less than `225`, the @boardname@ is mostly pointing **South**. ``||basic:Show||`` `S` on the @boardname@. @@ -69,7 +69,7 @@ basic.forever(function() { }) ``` -## Step 6 +## {Step 6} ``||logic:If||`` ``||variables:degrees||`` is less than `315`, the @boardname@ is mostly pointing **West**. ``||basic:Show||`` `W` on the @boardname@. @@ -89,7 +89,7 @@ basic.forever(function() { }) ``` -## Step 7 +## {Step 7} ``||logic:If||`` none of these conditions returned true, then the @boardname@ must be pointing **North** again. Display `N` on the @boardname@. @@ -114,7 +114,7 @@ basic.forever(function() { }) ``` -## Step 8 @unplugged +## Download @unplugged If you have a @boardname@, click `|Download|` and follow the screen instructions. You will have to follow the screen instructions to calibrate your compass. diff --git a/docs/projects/spy/dice.md b/docs/projects/spy/dice.md index 1d38958556c..78dbc76a091 100644 --- a/docs/projects/spy/dice.md +++ b/docs/projects/spy/dice.md @@ -2,18 +2,15 @@ ### @explicitHints true -## Introduction @unplugged +## {Introduction @unplugged} -Let's turn the @boardname@ into a dice! -(Want to learn how the accelerometer works? [Watch this video](https://youtu.be/byngcwjO51U)). - -We need 3 pieces of code: one to detect a throw (shake), another to pick a random number, and then one to show the number. +Let's create some digital 🎲 dice 🎲 with our micro:bit! ![A @boardname@ dice](/static/mb/projects/dice.png) -## Step 1 +## {Step 1} -Add an event to run code when a ``||input:shake gesture||`` is detected. +We'll "roll" our dice when we shake the micro:bit. Add an ``||input:on gesture shake||`` function. Type the code below, or drag a code snippet from the ``||input:Input||`` Toolbox category. ```spy input.onGesture(Gesture.Shake, function() { @@ -21,9 +18,9 @@ input.onGesture(Gesture.Shake, function() { }) ``` -## Step 2 +## {Step 2} -Put code in the event to ``||basic:show a number||`` when ``||input:on shake||`` happens. +Write some code to show a number in the ``||input:on shake||`` function, using the basic ``||basic:show number||`` function. ```spy input.onGesture(Gesture.Shake, function() { @@ -31,9 +28,9 @@ input.onGesture(Gesture.Shake, function() { }) ``` -## Step 3 +## {Step 3} -Pick a ``||math:pick a random||`` number and ``||basic:show||`` it on the screen. +Instead of showing 0, use the ``||math:randint||`` function to show a random number between a minimum and maximum value. ```spy input.onGesture(Gesture.Shake, function() { @@ -41,9 +38,9 @@ input.onGesture(Gesture.Shake, function() { }) ``` -## Step 4 +## {Step 4} -A typical dice shows values from `1` to `6`. Change the minimum and maximum values in ``||math:pick random||`` to ``1`` and ``6``! +A typical dice shows values from 1 to 6 dots. So, in the ``||math:randint||`` function, change the minimum value to **1** and the maximum value to **6**. ```spy input.onGesture(Gesture.Shake, function() { @@ -51,10 +48,13 @@ input.onGesture(Gesture.Shake, function() { }) ``` -## Step 5 +## {Step 5} + +Press the white **SHAKE** button on the micro:bit simulator. Do you see random numbers between 1 and 6 appear? ⭐ Great job! ⭐ -Use the simulator to try out your code. Does it show the number you expected? +## {Step 6} -## Step 6 +If you have a @boardname@ device, connect it to your computer and click the ``|Download|`` button. Follow the instructions to transfer your code onto the @boardname@. Once your code has been downloaded, attach your micro:bit to a battery pack and use it as digital 🎲 dice for your next boardgame! -If you have a @boardname@ connected, click ``|Download|`` and transfer your code to the @boardname@! \ No newline at end of file +## {Step 7} +Go further - Try adding some Music blocks to make a sound when you shake your dice, or use the micro:bit LED lights to show number values. Want to learn how the micro:bit motion detector or accelerometer works? [Watch this video](https://youtu.be/byngcwjO51U). \ No newline at end of file diff --git a/docs/projects/spy/flashing-heart.md b/docs/projects/spy/flashing-heart.md index 400f84afc9f..41d763a83c3 100644 --- a/docs/projects/spy/flashing-heart.md +++ b/docs/projects/spy/flashing-heart.md @@ -2,24 +2,23 @@ ### @explicitHints true -## Introduction @unplugged +## Code a Flashing Heart @unplugged -Learn how to use the LEDs and make a flashing heart! -(Want to learn how lights work? [Watch this video](https://youtu.be/qqBmvHD5bCw)). +Code the lights on the micro:bit into a flashing heart animation! 💖 ![Heart shape in the LEDs](/static/mb/projects/flashing-heart/sim.gif) -## Step 1 @fullscreen +## {Step 1 @fullscreen} -Make the screen ``||basic:show an icon||`` of a **Heart**. +Use the basic ``||basic:show icon||`` function to display the **HEART** icon. Type the code below, or drag a code snippet from the ``||basic:Basic||`` Toolbox category. ```spy basic.showIcon(IconNames.Heart) ``` -## Step 2 +## {Step 2} -After the icon is displayed, ``||basic:clear screen||`` and ``||basic:pause||`` for `500` milliseconds. +Use the ``||basic:clear screen||`` function followed by the ``||basic:pause||`` function to turn off the lights for **500** milliseconds (or half a second). ```spy basic.showIcon(IconNames.Heart) @@ -27,9 +26,9 @@ basic.clearScreen() basic.pause(500) ``` -## Step 3 +## {Step 3} -Now, copy the code currently have and add it to the end. In the copied code, ``||basic:show an icon||`` of a **SmallHeart**. Your heart will flash from big to small. +Copy the code you've written and paste it at the end. In the copied code, change the ``||basic:Icon Names||`` to a **SMALL HEART**. Run your code in the on-screen micro:bit simulator. Do you see a big and small heart animation? ```spy basic.showIcon(IconNames.Heart) @@ -40,23 +39,9 @@ basic.clearScreen() basic.pause(500) ``` -## Step 4 +## {Step 4} -Add one more ``||basic:show icon||`` at the end to display another **Heart**. - -```spy -basic.showIcon(IconNames.Heart) -basic.clearScreen() -basic.pause(500) -basic.showIcon(IconNames.SmallHeart) -basic.clearScreen() -basic.pause(500) -basic.showIcon(IconNames.Heart) -``` - -## Step 5 - -Do you want your heart to flash continuously? Remove the last ``||basic:show icon||`` and then put a ``||basic:forever||`` loop around your code. +Now let's make our hearts flash forever! Put a ``||basic:forever||`` loop around your code. ```spy basic.forever(function() { @@ -69,6 +54,6 @@ basic.forever(function() { }) ``` -## Step 6 +## {Step 5} -If you have a @boardname@ connected, click ``|Download|`` and transfer your code to the @boardname@! \ No newline at end of file +If you have a @boardname@ device, connect it to your computer and click the ``|Download|`` button. Follow the instructions to transfer your code onto the @boardname@ and watch the hearts flash! ⭐ Great job! ⭐ \ No newline at end of file diff --git a/docs/projects/spy/heads-guess.md b/docs/projects/spy/heads-guess.md index 23bc5495237..55b5c967424 100644 --- a/docs/projects/spy/heads-guess.md +++ b/docs/projects/spy/heads-guess.md @@ -2,12 +2,12 @@ ### @explicitHints true -## Introduction @unplugged +## Make a Heads Up guessing game! @unplugged This is a simple remake of the famous **Heads Up!** game. The player holds the @boardname@ on the forehead and has 30 seconds to guess words displayed on the screen. If the guess is correct, the player tilts the @boardname@ forward; to pass, the player tilts it backwards. -## Step 1 +## {Step 1} Put in code to ``||game:start a countdown||`` of 30 seconds. @@ -15,9 +15,9 @@ Put in code to ``||game:start a countdown||`` of 30 seconds. game.startCountdown(30000) ``` -## Step 2 +## {Step 2} -Create an ``||array:array||`` called `text_list` of words to guess. Arrays are also called lists. +Create an ``||arrays:array||`` called `text_list` of words to guess. Arrays are also called lists. ```spy let text_list: string[] = [] @@ -25,7 +25,7 @@ text_list = ["PUPPY", "CLOCK", "NIGHT"] game.startCountdown(30000) ``` -## Step 3 +## {Step 3} Add an event to run code when a ``||input:gesture||`` points the @boardname@ ``||input:logo up||``. This is the gesture to get a new word. @@ -36,7 +36,7 @@ input.onGesture(Gesture.LogoUp, function () { }) ``` -## Step 4 +## {Step 4} The items in the ``||arrays:text list||`` are numbered ``0`` to ``length - 1``. Add code to pick a ``||math:random||`` ``||variables:index||``. @@ -50,7 +50,7 @@ input.onGesture(Gesture.LogoUp, function () { }) ``` -## Step 5 +## {Step 5} Add code to ``||basic:show||`` the value of the item stored at ``||variables:index||`` in ``||arrays:text list||``. @@ -65,7 +65,7 @@ input.onGesture(Gesture.LogoUp, function () { }) ``` -## Step 6 +## {Step 6} Use an event to run code when a gesture has the @boardname@ ``||input:screen||`` is pointing ``||input:down||``. This is the gesture for a correct guess. @@ -76,7 +76,7 @@ input.onGesture(Gesture.ScreenDown, function () { }) ``` -## Step 7 +## {Step 7} Put in code to add points to the ``||game:score||``. @@ -87,9 +87,9 @@ input.onGesture(Gesture.ScreenDown, function () { }) ``` -## Step 8 +## {Step 8} -Add anonther event to run code when a gesture has the @boardname@ ``||input:screen||`` is +Add another event to run code when a gesture has the @boardname@ ``||input:screen||`` is pointing ``||input:up||``. This is the gesture for a pass. ```spy @@ -98,7 +98,7 @@ input.onGesture(Gesture.ScreenUp, function () { }) ``` -## Step 9 +## {Step 9} For the pass gesture, add code to remove a ``||game:life||`` from the player. diff --git a/docs/projects/spy/hot-potato.md b/docs/projects/spy/hot-potato.md index 129c510db32..75d05c0bb5d 100644 --- a/docs/projects/spy/hot-potato.md +++ b/docs/projects/spy/hot-potato.md @@ -1,13 +1,14 @@ # Hot Potato ### @explicitHints true +### @diffs true -## Introduction @unplugged +## Pass off that potato! @unplugged In this game, you will start a timer with a random countdown of a number of seconds. When the timer is off, the game is over and whoever is holding the potato has lost! Watch the tutorial on the [MakeCode YouTube channel](https://youtu.be/xLEy1B_gWKY). -## Step 1 +## {Step 1} Add an event to run code when ``||input:button A is pressed||``. @@ -17,7 +18,7 @@ input.onButtonPressed(Button.A, function () { }) ``` -## Step 2 +## {Step 2} Make a variable named ``||variables:timer||`` and set it to a ``||math:random value||`` between ``5`` and ``15``. @@ -28,12 +29,11 @@ is caught holding the potato. ```spy let timer = 0 input.onButtonPressed(Button.A, function () { - // @highlight timer = randint(5, 15) }) ``` -## Step 3 +## {Step 3} Add code to ``||basic:show an icon||`` to indicate that the game has started. @@ -41,65 +41,49 @@ Add code to ``||basic:show an icon||`` to indicate that the game has started. let timer = 0 input.onButtonPressed(Button.A, function () { timer = randint(5, 15) - // @highlight basic.showIcon(IconNames.Chessboard) }) ``` -## Step 4 - -Put in a ``||loops:while||`` loop to repeat code while the value in ``||variables:timer||`` is -greater than `0`. When `timer` value becomes `0` or less, the game is over. +## {Step 4} +Put in a ``||basic:pause||`` to wait the number of seconds set in the variable ``||variables:timer||``. When the ``||basic:pause||`` completes, the game is over. ```spy let timer = 0 input.onButtonPressed(Button.A, function () { timer = randint(5, 15) basic.showIcon(IconNames.Chessboard) - // @highlight - while (timer > 0) { - } + basic.pause(1000 * timer) }) ``` -## Step 5 +## {Step 5} -Inside the ``||loops:while||`` loop, add code to ``||variables:decrease||`` the timer -``||basic:every second||``. +**After** the ``||basic:pause||``, add code to ``||basic:show||`` that the game is over. ```spy let timer = 0 input.onButtonPressed(Button.A, function () { timer = randint(5, 15) basic.showIcon(IconNames.Chessboard) - while (timer > 0) { - // @highlight - timer += -1 - // @highlight - basic.pause(1000) - } + basic.pause(1000 * timer) + basic.showIcon(IconNames.Skull) }) ``` -## Step 5 +## {Step 6} -**After** the ``||loops:while||`` loop is done, add code to ``||basic:show||`` that the game is over. +You can simplify your code by replacing ``||variables:timer||`` in the ``||basic:pause||`` with a ``||math:random value||`` between ``5`` and ``15``. Now, delete the other lines using the ``||variables:timer||`` variable. ```spy -let timer = 0 input.onButtonPressed(Button.A, function () { - timer = randint(5, 15) basic.showIcon(IconNames.Chessboard) - while (timer > 0) { - timer += -1 - basic.pause(1000) - } - // @highlight + basic.pause(1000 * randint(5, 15)) basic.showIcon(IconNames.Skull) }) ``` -## Step 6 +## {Step 7} `|Download|` your code to your @boardname@, tape it to a potato and play the game with your friends! diff --git a/docs/projects/spy/level.md b/docs/projects/spy/level.md index a4e090ecfe2..d15343596de 100644 --- a/docs/projects/spy/level.md +++ b/docs/projects/spy/level.md @@ -2,14 +2,14 @@ ### @explicitHints true -## Introduction @unplugged +## Is it level? @unplugged Is your table flat? Use the @boardname@ as a level! ![A level drawing](/static/mb/projects/level.png) -## Step 1 +## {Step 1} Make a variable ``||variables:x||`` and store the ``||input:acceleration x||`` value in the ``||basic:forever||`` loop. @@ -21,7 +21,7 @@ basic.forever(function() { }) ``` -## Step 2 +## {Step 2} Make another variable ``||variables:y||`` and store the ``||input:acceleration y||`` value. @@ -33,7 +33,7 @@ basic.forever(function() { }) ``` -## Step 3 +## {Step 3} Add a code to test ``||logic:if||`` the ``||Math:absolute value||`` of ``||variables:x||`` is ``||logic:greater than||`` ``32``. If it is true, ``||basic:show an icon||`` to tell you that the @boardname@ is not flat, ``||logic:else||`` show nothing, for now. @@ -51,7 +51,7 @@ basic.forever(function() { }) ``` -## Step 4 +## {Step 4} Add an ``||logic:else if||`` to check that the ``||Math:absolute value||`` of ``||variables:y||`` is ``||logic:greater than||`` ``32``. If it is true, ``||basic:show an icon||`` that tells you the @boardname@ is not flat. @@ -71,7 +71,7 @@ basic.forever(function() { }) ``` -## Step 5 +## {Step 5} The code under the ``||logic:else||`` will run if both acceleration ``x`` and ``y`` are small, which happens when the @boardname@ is laying flat. Add code to ``||basic:show a happy image||``. @@ -90,7 +90,7 @@ basic.forever(function() { }) ``` -## Step 6 +## {Step 6} If you have a @boardname@ connected, click ``|Download|`` to transfer your code! Try it out on a table, counter, or window sill in your house! diff --git a/docs/projects/spy/love-meter.md b/docs/projects/spy/love-meter.md index ac60574a9c2..9ae6b4a2dd4 100644 --- a/docs/projects/spy/love-meter.md +++ b/docs/projects/spy/love-meter.md @@ -2,48 +2,66 @@ ### @explicitHints true -## Introduction @unplugged +## {Introduction @unplugged} -Make a **LOVE METER** machine, how sweet! The @boardname@ is feeling the love, then sometimes not so much! +How much love 😍 are you emitting today? Create a 💓 LOVE METER 💓 machine with your micro:bit! ![Love meter banner message](/static/mb/projects/love-meter/love-meter.gif) -## Step 1 +## {Step 1} -Add an event to run code when ``||input:pin 0 is pressed||``. Use the ``P0`` touchpin. +Add an input ``||input:on pin pressed||`` function to run code when Pin P0 is pressed on the micro:bit. Type the code below, or drag a code snippet from the ``||input:Input||`` Toolbox category. ```spy input.onPinPressed(TouchPin.P0, function() { }) ``` -## Step 2 +## {Step 2} -Put code into the ``||input:pin 0 is pressed||`` event to ``||basic:show||`` a ``||Math:random number||`` -between `0` to `100` when pin **0** is pressed. +Write some code to show a number in the ``||input:on pin pressed||`` function, using the basic ``||basic:show number||`` function. ```spy input.onPinPressed(TouchPin.P0, function() { - basic.showNumber(randint(0, 100)); -}); + basic.showNumber(0) +}) ``` -## Step 3 +## {Step 3} -Click on pin **0** in the simulator and see which number is chosen. +Instead of showing 0, use the ``||math:randint||`` function to show a random number between a minimum and maximum value. -## Step 4 +```spy +input.onPinPressed(TouchPin.P0, function() { + basic.showNumber(randint(0, 10)) +}) +``` + +## {Step 4} -Insert code to ``||basic:show||`` the ``"LOVE METER"`` message on the screen when the program starts. +Everyone knows that love can be measured on a scale of 0 to 100. So, in the ``||math:randint||`` function, change the maximum value to **100**. + +```spy +input.onPinPressed(TouchPin.P0, function() { + basic.showNumber(randint(0, 100)) +}) +``` + +## {Step 5} + +Now let's be sure to label our Love Machine! Use the basic ``||basic:show string||`` function to show the message "LOVE METER" on the screen of the micro:bit. ```spy basic.showString("LOVE METER") input.onPinPressed(TouchPin.P0, function() { basic.showNumber(randint(0, 100)); -}); +}) ``` -## Step 5 +## {Step 6} + +Let's test our code. Press **Pin 0** on the micro:bit on-screen simulator (bottom left). Numbers between 0-25 = 🖤 No Love, 26-50 = đŸĢļ BFF Love, 51-75 = 💘 Brokenhearted Love, 76-100 = 💖đŸ”Ĩ Fiery Hot Love! + +## {Step 7} -Click ``|Download|`` to transfer your code in your @boardname@. Hold the **GND** pin with one hand -and press pin **0** with the other hand to trigger this code. \ No newline at end of file +If you have a @boardname@ device, connect it to your computer and click the ``|Download|`` button. Follow the instructions to transfer your code onto the @boardname@. Once your code has been downloaded, hold the **GND** pin with one hand and touch the **0** pin with the other hand. Your micro:bit 💓 LOVE METER 💓 machine will detect the love current flowing through your body! \ No newline at end of file diff --git a/docs/projects/spy/micro-chat.md b/docs/projects/spy/micro-chat.md index 716499163c6..1035f35d188 100644 --- a/docs/projects/spy/micro-chat.md +++ b/docs/projects/spy/micro-chat.md @@ -2,68 +2,81 @@ ### @explicitHints true -## Introduction @unplugged +## {Introduction @unplugged} ![Two @boardname@ connected via radio](/static/mb/projects/a9-radio.png) -Use the **radio** to send and receive messages with another @boardname@. +Use the micro:bit đŸ“ģ radio to send and receive đŸ’Ŧ messages between micro:bits! -## Handle buttons +## {Step 1} -Add an event to run code when ``||input:button A is pressed||``. +Let's write some code to set the channel over which we'll send messages. Only micro:bits who are in the same group will be able to send and receive messages between them. Use the radio ``||radio:set group||`` function. Type the code below, or drag a code snippet from the ``||radio:Radio||`` Toolbox category. ```spy +radio.setGroup(1) +``` + +## {Step 2} + +Now let's send a message when we press a button on our micro:bit. Use the input ``||input:on button pressed||`` function. + +```spy +radio.setGroup(1) input.onButtonPressed(Button.A, function() { }) ``` -## Sending a message +## {Step 3} -Add code to ``||radio:send a string||`` over ``||radio:radio||`` when ``||input:button A is pressed||``. -Every @boardname@ nearby will receive this message. +Add code to ``||radio:send a string||`` in the ``||input:on button pressed||`` function. This message will be sent to every micro:bit nearby in group 1. ```spy +radio.setGroup(1) input.onButtonPressed(Button.A, function() { // @highlight - radio.sendString(":)") + radio.sendString("Micro Chat!") }) ``` -## Receiving a message +## {Step 4} -Put in another event to run code when a ``||radio:string is received||`` over ``||radio:radio||``. +Now let's add some code to receive messages. Use the radio ``||radio:on received string||`` function. ```spy -radio.onReceivedString(function (receivedString) { +radio.setGroup(1) +input.onButtonPressed(Button.A, function() { + radio.sendString("Micro Chat!") +}) +radio.onReceivedString(function(receivedString: string) { }) ``` -## Displaying text +## {Step 5} -Inside the event, add code to ``||basic:show||`` the ``||variables:receivedString||``. +Inside the ``||radio:on received string||`` function, use the basic ``||basic:show string||`` function to show the value in the ``||variables:receivedString||`` variable. ```spy -radio.onReceivedString(function (receivedString) { +radio.setGroup(1) +input.onButtonPressed(Button.A, function() { + radio.sendString("Micro Chat!") +}) +radio.onReceivedString(function(receivedString: string) { // @highlight basic.showString(receivedString) }) ``` -## Testing in the simulator +## {Step 6} -Press button **A** on the simulator, you will notice that a second @boardname@ appears (if your screen is too small, the simulator might decide not to show it). Try pressing **A** again and notice that the ":)" message gets displayed on the other @boardname@. +Let's test our code! In the micro:bit on-screen simulator, press button **A**. You should see a second @boardname@ appear. Now try pressing **A** again. Do you see your message appear on the second micro:bit? ⭐ Great job! ⭐ -## Try it for real +## {Step 7} -If you two @boardname@s, download the program to each one. Press button **A** on one and see if the other gets a message. +If you have a @boardname@ device, connect it to your computer and click the ``|Download|`` button. Follow the instructions to transfer your code onto the @boardname@. If you have two micro:bits, download the program to each one. Press button **A** on one and see if the other gets the message! -## Groups +## {Step 8} -Add code to ``||radio:set the group||`` number of your program. You will only receive messages from @boardname@s within the same group. Use this to avoid receiving messages from every @boardname@ that is transmitting. - -```spy -radio.setGroup(123) -``` +Go further - try using different buttons to send a mix of messages 📝, or send secret 🔒 messages to different radio groups! ```package radio diff --git a/docs/projects/spy/name-tag.md b/docs/projects/spy/name-tag.md index 89f9c07c0d5..49352c5cfb6 100644 --- a/docs/projects/spy/name-tag.md +++ b/docs/projects/spy/name-tag.md @@ -2,32 +2,32 @@ ### @explicitHints true -## Introduction @unplugged +## Turn your micro:bit into a digital name tag @unplugged -Tell everyone who you are. Show you name on the LEDs. +See your name in 💡 lights! 💡 Code the micro:bit to scroll your name across the screen. ![Name scrolling on the LEDs](/static/mb/projects/name-tag/name-tag.gif) -## Step 1 +## {Step 1} -On the screen, ``||basic:show a string||`` saying `"My name is: "`. +Use the basic ``||basic:show string||`` function to display the text `"My name is: "`. Type the code below, or drag a code snippet from the ``||basic:Basic||`` Toolbox category. ```spy basic.showString("My name is: ") ``` -## Step 2 +## {Step 2} -Let everyone know who you are and ``||basic:show a string||`` with your name. +Add another ``||basic:show a string||`` line of code, and type in your first name. ```spy basic.showString("My name is: ") basic.showString("Sarah!") ``` -## Step 3 +## {Step 3} -Tell everyone what your age is. Add a ``||basic:show string||`` for `"My age is "` and then ``||basic:show a number||`` for your age. +Add another ``||basic:show string||`` line of code, and display the text `"My age is: "`. Then use the basic ``||basic:show number||`` function to display your age. ```spy basic.showString("My name is: ") @@ -36,23 +36,14 @@ basic.showString("My age is: ") basic.showNumber(9) ``` -## Step 4 +## {Step 4} -Look at the simulator and make sure it shows your name and age on the screen. +Look at the @boardname@ simulator on the screen. Do you see your name and age? ⭐ Great job! ⭐ You've turned the micro:bit into a digital name tag! -## Step 5 +## {Step 5} -Place more ``||basic:show strings||`` to create a longer message. +If you have a @boardname@ device, connect it to your computer and click the ``|Download|`` button. Follow the instructions to transfer your code onto the @boardname@ and watch your name and age appear in lights! -```spy -basic.showString("My name is: ") -basic.showString("Sarah!") -basic.showString("My age is: ") -basic.showNumber(9) -basic.showString("Favorite color: ") -basic.showString("Blue") -``` - -## Step 6 +## {Step 6} -If you have a @boardname@ connected, click ``|Download|`` and transfer your code to the @boardname@! +Go further - try adding more ``||basic:show string||`` and ``||basic:show number||`` functions to tell people more about yourself (favorite color, lucky number). Learn more about how the @boardname@ lights work by watching [this video](https://youtu.be/qqBmvHD5bCw). diff --git a/docs/projects/spy/rock-paper-scissors.md b/docs/projects/spy/rock-paper-scissors.md index fb74c205238..918dc5cbb6f 100644 --- a/docs/projects/spy/rock-paper-scissors.md +++ b/docs/projects/spy/rock-paper-scissors.md @@ -2,135 +2,85 @@ ### @explicitHints true -## Introduction @unplugged +## {Introduction @unplugged} ![Cartoon of the Rock Paper Scissors game](/static/mb/projects/a4-motion.png) -Use the accelerometer and the screen to build a **Rock Paper Scissors** game that you can play with your friends! +Turn your micro:bit into a **Rock Paper Scissors** game that you can play with your friends! -## Step 1 @fullscreen +## {Step 1 @fullscreen} -Add an ``||input:on shake||`` event to run code when you shake the @boardname@. +We'll start our Rock Paper Scissors game when we shake 👋 our micro:bit. Add an ``||input:on shake||`` function to run code when you shake the @boardname@. Type the code below, or drag a code snippet from the ``||input:Input||`` Toolbox category. ```spy -input.onGesture(Gesture.Shake, () => { +input.onGesture(Gesture.Shake, function () { }) ``` -## Step 2 +## {Step 2} -Inside the ``||input:on shake||``, a choose a ``||math:random||`` number in the range of `1` to `3` -and store it in a variable named ``hand``. The random numbers are used to select a picture to show -on the LEDs. +Create a variable named "hand" - this will help us keep track of whether we have a Rock, Paper or Scissors in our hand. Then inside the ``||input:on shake||`` function, use the ``||math:randint||`` function to set the hand variable to a random number from **1** to **3** representing a Rock, Paper or Scissors. ```spy let hand = 0 -input.onGesture(Gesture.Shake, () => { +input.onGesture(Gesture.Shake, function () { hand = randint(1, 3) }) ``` -## Step 3 +## {Step 3} -``||logic:if||`` the ``||math:random||`` number in ``hand`` is `1`, ``||basic:show||`` a picture of a piece of paper on the ``||basic:LEDs||``. +To check the value of the hand variable, type ``||logic:if hand==1||`` then use the ``||basic:show icon||`` function to show a small square icon representing a 💎 Rock. ```spy let hand = 0 -input.onGesture(Gesture.Shake, () => { +input.onGesture(Gesture.Shake, function () { hand = randint(1, 3) if (hand == 1) { - basic.showLeds(` - # # # # # - # . . . # - # . . . # - # . . . # - # # # # # - `) + basic.showIcon(IconNames.SmallSquare) } }) ``` -## Step 4 @fullscreen +## {Step 4} -Click on the **SHAKE** button in the simulator. If you try enough times, you should see a picture of paper on the screen. - -![Shaking a @boardname@ simulator](/static/mb/projects/rock-paper-scissors/rpsshake.gif) - -## Step 5 @fullscreen - -``||logic:if||`` the ``||math:random||`` number is not `1`, then the number is something -``||logic:else||`` so ``||basic:show on the LEDs||`` a picture of some scissors. +Now add an ``||logic:else if||`` clause to check if the hand value is **2**. In that case, use the ``||basic:show icon||`` function to show a large square icon representing 📃 Paper. ```spy -let hand = 0 -input.onGesture(Gesture.Shake, () => { +let hand = 0; +input.onGesture(Gesture.Shake, function() { hand = randint(1, 3) if (hand == 1) { - basic.showLeds(` - # # # # # - # . . . # - # . . . # - # . . . # - # # # # # - `) - } else { - basic.showLeds(` - # # . . # - # # . # . - . . # . . - # # . # . - # # . . # - `) + basic.showIcon(IconNames.SmallSquare) + } else if (hand == 2) { + basic.showIcon(IconNames.Square) } }) ``` -## Step 6 +## {Step 5} -Now, when the ``||math:random||`` number in ``hand`` is `2` we want to ``||basic:show on the LEDs||`` a picture of a rock. Change the way you check the value for ``hand`` so that the picture is a piece of paper ``||logic:if||`` it is `1`, ``||logic:else if||`` it is `2` the picture is a rock, or ``||logic:else||`` the picture is scissors. +Finally let's deal with the last condition - if our hand variable isn't holding a 1 (Rock) or a 2 (Paper), then it must be 3 (Scissors)! Add an ``||logic:else||`` clause and use the ``||basic:show icon||`` function to show âœ‚ī¸ Scissors. ```spy -let hand = 0 -input.onGesture(Gesture.Shake, () => { +let hand = 0; +input.onGesture(Gesture.Shake, function() { hand = randint(1, 3) if (hand == 1) { - basic.showLeds(` - # # # # # - # . . . # - # . . . # - # . . . # - # # # # # - `) + basic.showIcon(IconNames.SmallSquare) } else if (hand == 2) { - basic.showLeds(` - . . . . . - . # # # . - . # # # . - . # # # . - . . . . . - `) + basic.showIcon(IconNames.Square) } else { - basic.showLeds(` - # # . . # - # # . # . - . . # . . - # # . # . - # # . . # - `) + basic.showIcon(IconNames.Scissors) } }) ``` -## Step 7 @fullscreen - -Click on the **SHAKE** button in the simulator and check to see that each image is showing up. - -![Shaking a @boardname@ simulator](/static/mb/projects/rock-paper-scissors/rpssim3.gif) +## {Step 6} -## Step 8 @fullscreen +Let's test your code! Press the white **SHAKE** button on the micro:bit on-screen simulator, or move your cursor quickly back and forth over the simulator. Do you see the icons for rock, paper and scissors randomly appear? ⭐ Great job! ⭐ -If you have a @boardname@, click on ``|Download|`` and follow the instructions to get the code -onto your @boardname@. Your game is ready! Gather your friends and play Rock Paper Scissors! +## {Step 7} -![A @boardname@ in a hand](/static/mb/projects/rock-paper-scissors/hand.jpg) +If you have a @boardname@ device, connect it to your computer and click the ``|Download|`` button. Follow the instructions to transfer your code onto the @boardname@. Once your code has been downloaded, attach your micro:bit to a battery pack and challenge another micro:bit or a human to a game of 💎 Rock, 📃 Paper, âœ‚ī¸ Scissors! diff --git a/docs/projects/spy/smiley-buttons.md b/docs/projects/spy/smiley-buttons.md index e820a0274f1..7273095adb8 100644 --- a/docs/projects/spy/smiley-buttons.md +++ b/docs/projects/spy/smiley-buttons.md @@ -2,27 +2,23 @@ ### @explicitHints true -## Introduction @unplugged +## Code a micro:bit emoji! @unplugged -Code the buttons on the @boardname@ to show that it's happy or sad. -(Want to learn how the buttons works? [Watch this video](https://youtu.be/t_Qujjd_38o)). +Program the buttons on the @boardname@ to show a happy 😀 or sad face 🙁 ![Pressing the A and B buttons](/static/mb/projects/smiley-buttons/sim.gif) -## Step 1 +## {Step 1} -Put in an ``||input:on button pressed||`` event to run code when button **A** is pressed. +Use the ``||input:on button pressed||`` function to run code when button **A** is pressed. Type the code below, or drag a code snippet from the ``||input:Input||`` Toolbox category. ```spy -input.onButtonPressed(Button.A, function() { -}) +input.onButtonPressed(Button.A, function() {}) ``` -## Step 2 - -Use ``||basic:show icon||`` to display a **Happy** face on the screen. +## {Step 2} -Press the **A** button in the simulator to see the smiley. +Use the basic ``||basic:show icon||`` statement inside the ``||input:on button pressed||`` function display a **Happy** face when button **A** is pressed. ```spy input.onButtonPressed(Button.A, function() { @@ -30,9 +26,13 @@ input.onButtonPressed(Button.A, function() { }) ``` -## Step 3 +## {Step 3} + +Run your code in the @boardname@ simulator on the screen, press the **A** button. Do you see a happy face? ⭐ Great job! ⭐ + +## {Step 4} -Use another ``||input:on button pressed||`` with a ``||basic:show icon||`` inside to display a **Sad** face when button **B** is pressed. +Write another ``||input:on button pressed||`` function with a ``||basic:show icon||`` inside to display a **Sad** face when button **B** is pressed. Try copying and pasting your existing code, and change **A** to **B** and Happy to Sad. ```spy input.onButtonPressed(Button.B, function() { @@ -40,21 +40,15 @@ input.onButtonPressed(Button.B, function() { }) ``` -## Step 4 +## {Step 5} -Add a secret mode that happens when **A** and **B** are pressed together. For this case, use ``||basic:show icon||`` multiple times to create an animation. - -```spy -input.onButtonPressed(Button.AB, function() { - basic.showIcon(IconNames.Silly) - basic.showIcon(IconNames.Surprised) -}) -``` +Run your code in the @boardname@ simulator on the screen, press the **B** button. Do you see a sad face? ⭐ Great job! ⭐ -## Step 5 +## {Step 6} -Click ``|Download|`` to transfer your code to your @boardname@ (if you have one). Try buttons **A**, **B** and then **A** and **B** together. +If you have a @boardname@ device, connect it to your computer and click the ``|Download|`` button. Follow the instructions to transfer your code onto the @boardname@. Try pressing the **A** and **B** buttons on the micro:bit to see your Happy 😀 and Sad 🙁 emojis! -## Step 6 +## {Step 7} -If you have a @boardname@ connected, click ``|Download|`` and transfer your code to the @boardname@! +Go further - try adding a secret emoji that appears when **A** and **B** buttons are pressed together! +Learn more about how the @boardname@ buttons work by watching [this video](https://youtu.be/t_Qujjd_38o). diff --git a/docs/projects/spy/snap-the-dot.md b/docs/projects/spy/snap-the-dot.md index 1ff43110a26..0a645f2e9a8 100644 --- a/docs/projects/spy/snap-the-dot.md +++ b/docs/projects/spy/snap-the-dot.md @@ -69,8 +69,8 @@ input.onButtonPressed(Button.A, function () { }) basic.forever(function () { sprite.move(1) - basic.pause(100) sprite.ifOnEdgeBounce() + basic.pause(100) }) ``` @@ -90,8 +90,8 @@ input.onButtonPressed(Button.A, function () { }) basic.forever(function () { sprite.move(1) - basic.pause(100) sprite.ifOnEdgeBounce() + basic.pause(100) }) ``` diff --git a/docs/projects/spy/stopwatch.md b/docs/projects/spy/stopwatch.md index c25f660b1c7..d641ae31e04 100644 --- a/docs/projects/spy/stopwatch.md +++ b/docs/projects/spy/stopwatch.md @@ -8,7 +8,7 @@ This project turns the @boardname@ into a simple stopwatch. Pressing **A** starts the timer. Pressing **B** displays the elapsed seconds. -## Step 1 +## {Step 1} Add an event to run code when ``||input:button A is pressed||``. @@ -17,7 +17,7 @@ input.onButtonPressed(Button.A, function () { }) ``` -## Step 2 +## {Step 2} Add code inside the ``||input:button A is pressed||`` event to store the current ``||input:running time||`` in a variable ``||variables:start||``. This is the start time. @@ -29,7 +29,7 @@ input.onButtonPressed(Button.A, function () { }) ``` -## Step 3 +## {Step 3} Add another event to run code when ``||input:button B is pressed||``. @@ -38,7 +38,7 @@ input.onButtonPressed(Button.B, function () { }) ``` -## Step 4 +## {Step 4} Add code in that event to compute the difference between the ``||input:running time||`` and ``||variables:value||`` time. This is the elapsed number of milliseconds since @@ -51,7 +51,7 @@ input.onButtonPressed(Button.B, function () { }) ``` -## Step 5 +## {Step 5} After setting the ``||variables:elapsed||`` time, add code to ``||basic:show||`` the number of milliseconds ``||variables:elapsed||``. Use ``||Math:integer division||`` to @@ -65,11 +65,11 @@ input.onButtonPressed(Button.B, function () { }) ``` -## Step 6 +## {Step 6} Try your program in the simulator. Press **A** to start the stopwatch and press **B** to get the current elapsed time. You can press **B** multiple times. -## Step 7 +## {Step 7} If you have a @boardname@ connected, click ``|Download|`` to transfer your code! diff --git a/docs/projects/spy/tug-of-led.md b/docs/projects/spy/tug-of-led.md index bcd60376407..aa35dcf0199 100644 --- a/docs/projects/spy/tug-of-led.md +++ b/docs/projects/spy/tug-of-led.md @@ -9,7 +9,7 @@ Instead of a rope, we'll use the LED screen by pulling the LED light through the ![A micro:bit holding a rope](/static/mb/projects/tug-of-led.png) -## Step 1 +## {Step 1} Create a new variable ``||variables:rope||`` to track the progress of the game. The ``||variables:rope||`` variable will be used as the **x** coordinate of the LED to lit so we set it to ``2`` to start. @@ -18,9 +18,9 @@ variable will be used as the **x** coordinate of the LED to lit so we set it to let rope = 2 ``` -## Step 2 +## {Step 2} -Add a ``||basic:forever||`` loop that turns on the LED at the position set in ``||variables:rope||``. +Add a ``||basic:forever||`` loop that will ``||basic:clear screen||`` and turn on the LED at the position set in ``||variables:rope||``. ```spy let rope = 2 @@ -30,7 +30,7 @@ basic.forever(function() { }) ``` -## Step 3 +## {Step 3} Add an event for ``||input:button A pressed||`` to change the ``||variables:rope||`` value by **-0.1**. @@ -41,7 +41,7 @@ input.onButtonPressed(Button.A, function () { }) ``` -## Step 4 +## {Step 4} Add an event on ``||input:button B pressed||`` to change the ``||variables:rope||`` value by **0.1**. @@ -52,7 +52,7 @@ input.onButtonPressed(Button.B, function () { }) ``` -## Step 5 +## {Step 5} Because a button press pulls the rope by **0.1** in either direction, plot the ``||math:rounded||`` value of ``||variables:rope||`` to the nearest LED. @@ -64,7 +64,7 @@ basic.forever(function() { }) ``` -## Step 6 +## {Step 6} Back in the ``||basic:forever||``, add code to test ``||logic:if||`` the ``||variables:rope||`` is negative then ``||basic:show||`` **A WINS** on the screen. @@ -81,7 +81,7 @@ basic.forever(function() { }) ``` -## Step 7 +## {Step 7} Add an ``||logic:else if||`` condition to test ``||logic:if||`` the ``||variables:rope||`` is greater than `4` then ``||basic:show||`` **B WINS** on the screen. @@ -100,6 +100,6 @@ basic.forever(function() { }) ``` -## Step 8 +## {Step 8} Find a friend and start button smashing! \ No newline at end of file diff --git a/docs/projects/states-of-matter/code.md b/docs/projects/states-of-matter/code.md index 2f324f6b2cc..9f848b85735 100644 --- a/docs/projects/states-of-matter/code.md +++ b/docs/projects/states-of-matter/code.md @@ -20,7 +20,7 @@ We want to detect when the solid state occurs. On Pin 2 Pressed, you want to rep ```blocks let temperature = 0 let atmos_temperature = 0 -input.onPinPressed(TouchPin.P2, () => { +input.onPinPressed(TouchPin.P2, function () { atmos_temperature = 0 basic.showString("SOLID") }) @@ -35,11 +35,11 @@ We want to detect when the liquid state happens. On Pin 1 Pressed, you want to r ```blocks let temperature = 0 let atmos_temperature = 0 -input.onPinPressed(TouchPin.P2, () => { +input.onPinPressed(TouchPin.P2, function () { atmos_temperature = 0 basic.showString("SOLID") }) -input.onPinPressed(TouchPin.P1, () => { +input.onPinPressed(TouchPin.P1, function () { atmos_temperature = 80 basic.showString("LIQUID") }) @@ -54,15 +54,15 @@ We want to detect when matter will be a gas. On Pin 0 Pressed, you want to repre ```blocks let atmos_temperature = 0 let temperature = 0 -input.onPinPressed(TouchPin.P0, () => { +input.onPinPressed(TouchPin.P0, function () { atmos_temperature = 250 basic.showString("GAS") }) -input.onPinPressed(TouchPin.P2, () => { +input.onPinPressed(TouchPin.P2, function () { atmos_temperature = 0 basic.showString("SOLID") }) -input.onPinPressed(TouchPin.P1, () => { +input.onPinPressed(TouchPin.P1, function () { atmos_temperature = 80 basic.showString("LIQUID") }) @@ -79,19 +79,19 @@ We want to display a change of temperature on shake. When you shake the states o ```blocks let atmos_temperature = 0 let temperature = 0 -input.onGesture(Gesture.Shake, () => { +input.onGesture(Gesture.Shake, function () { temperature += 50 basic.showIcon(IconNames.Triangle) }) -input.onPinPressed(TouchPin.P0, () => { +input.onPinPressed(TouchPin.P0, function () { atmos_temperature = 250 basic.showString("GAS") }) -input.onPinPressed(TouchPin.P2, () => { +input.onPinPressed(TouchPin.P2, function () { atmos_temperature = 0 basic.showString("SOLID") }) -input.onPinPressed(TouchPin.P1, () => { +input.onPinPressed(TouchPin.P1, function () { atmos_temperature = 80 basic.showString("LIQUID") }) @@ -117,11 +117,11 @@ The second condition follows this logic: ```blocks let atmos_temperature = 0 let temperature = 0 -input.onGesture(Gesture.Shake, () => { +input.onGesture(Gesture.Shake, function () { temperature += 50 basic.showIcon(IconNames.Triangle) }) -basic.forever(() => { +basic.forever(function () { if (temperature < atmos_temperature) { temperature += 20 } else { @@ -137,15 +137,15 @@ basic.forever(() => { basic.clearScreen() basic.pause(100) }) -input.onPinPressed(TouchPin.P0, () => { +input.onPinPressed(TouchPin.P0, function () { atmos_temperature = 250 basic.showString("GAS") }) -input.onPinPressed(TouchPin.P2, () => { +input.onPinPressed(TouchPin.P2, function () { atmos_temperature = 0 basic.showString("SOLID") }) -input.onPinPressed(TouchPin.P1, () => { +input.onPinPressed(TouchPin.P1, function () { atmos_temperature = 80 basic.showString("LIQUID") }) diff --git a/docs/projects/step-counter.md b/docs/projects/step-counter.md index 27c5be45acb..527c8e2e160 100644 --- a/docs/projects/step-counter.md +++ b/docs/projects/step-counter.md @@ -1,65 +1,86 @@ # Step Counter -## Introduction @unplugged +## Count your steps with the micro:bit! @unplugged ![A @boardname@ attached on a foot](/static/mb/projects/step-counter.png) -This project turns the @boardname@ into a simple step counter. A step counter is also known as a pedometer. Each **shake** event increments a **counter** variable. The step count is displayed on the LEDs. +Turn your @boardname@ into a step counter (also known as a pedometer). We'll use the motion sensor (also known as an accelerometer) to measure when we take a step with the micro:bit. -If you built a watch in the [make](/projects/watch/make) portion of the of the [Watch](/projects/watch) project, you can use the code from this project with it too. +## {Step 1} -## A counter +First we need to create a variable to keep track of the number of steps đŸĻļ. A variable is a container for storing values. +Click on the ``||variables:Variables||`` category in the Toolbox. Click on the **Make a Variable** button. Give your new variable the name "steps" and click Ok. -To build a counter, we'll need a variable ``||variables:step||`` to store the number of steps. +## {Step 2} + +Click on the ``||variables:Variables||`` category in the Toolbox. You'll notice that there are some new blocks that have appeared. Drag a ``||variables:set steps||`` block into the ``||basic:on start||`` block. This sets the value of our ``||variables:steps||`` variable to **0** when our program starts. + +```blocks +let steps = 0 +``` + +## {Step 3} + +Let's record a step every time our micro:bit shakes. Click on the ``||input:Input||`` category in the Toolbox. Drag an ``||input:on shake||`` block out to the workspace and place it anywhere. ```blocks -let step = 0 -step = 0 +input.onGesture(Gesture.Shake, function () {}) ``` -## Detecting a step +## {Step 4} -Assuming you attach the @boardname@ to your foot or ankle, it will get shaken when you take a step. We can use the ``||input:on shake||`` event to detect a step (it should notice a step most of the time). Let's add the code to increment ``||variables:step||`` by `1` when the @boardname@ is shaken. +Click on the ``||variables:Variables||`` category in the Toolbox. Drag a ``||variables:change steps||`` block into the ``||input:on shake||`` block. Now every time we shake our micro:bit (or take a step), we will add 1 to the value in our ``||variables:steps||`` variable. ```blocks -let step = 0 +let steps = 0 input.onGesture(Gesture.Shake, function () { - step += 1 + steps += 1 }) -step = 0 ``` -## How many steps so far? +## {Step 5} -We want to always see how many steps were counted. In a ``||basic:forever||`` loop, we add a ``||basic:show number||`` block to display the value of ``step``. +Let's show the number of steps taken. Click on the ``||basic:Basic||`` category in the Toolbox. Drag a ``||basic:show number||`` block into the ``||input:on shake||`` block, underneath the ``||variables:change steps||`` block. ```blocks -let step = 0 +let steps = 0 input.onGesture(Gesture.Shake, function () { - step += 1 + steps += 1 + basic.showNumber(0) }) -basic.forever(function() { - basic.showNumber(step) +``` + +## {Step 6} + +Click on the ``||variables:Variables||`` category in the Toolbox. Drag a ``||variables:steps||`` block into the ``||basic:show number||`` block, replacing the number **0**. + +```blocks +let steps = 0 +input.onGesture(Gesture.Shake, function () { + steps += 1 + basic.showNumber(steps) }) -step = 0 ``` -## Display lag +## {Step 7} + +Let's test your code! Press the white **SHAKE** button on the micro:bit on-screen simulator, or move your cursor quickly back and forth over the simulator. Do you see the number of steps increasing on the micro:bit? ⭐ Great job! ⭐ + +## {Step 8} -Did you notice there is a lag, or delay, in the display of steps? This is because the ``step`` value can change **while** the @boardname@ is displaying a number. To remove the lag, add ``||led:stop animation||`` after changing the value of ``step``. +If you have a @boardname@ device, connect it to your computer and click the ``|Download|`` button. Follow the instructions to transfer your code onto the @boardname@. Once your code has been downloaded, attach your micro:bit to a battery pack and put in your sock. Walk around. Is the micro:bit counting your steps? + +## {Step 9} + +Go further - you may notice the micro:bit can't count as fast you might run. That's because there is a delay while the micro:bit is displaying numbers. To correct for this, click on the Hint to see an alternate solution. Learn more about how the @boardname@ acccelerometer works by watching [this video](https://youtu.be/byngcwjO51U). ```blocks -let step = 0 +let steps = 0 input.onGesture(Gesture.Shake, function () { - step += 1 + steps += 1 led.stopAnimation() }) basic.forever(function() { - basic.showNumber(step) + basic.showNumber(steps) }) -step = 0 -``` - -## Run! - -Strap the @boardname@ on your leg and run around to see if it counts your steps! \ No newline at end of file +``` \ No newline at end of file diff --git a/docs/projects/stopwatch.md b/docs/projects/stopwatch.md index a0bb0ed1373..be4aa97fea6 100644 --- a/docs/projects/stopwatch.md +++ b/docs/projects/stopwatch.md @@ -1,21 +1,21 @@ # Stopwatch -## Introduction @unplugged +## Time is ticking! @unplugged ![A @boardname@ stopwatch toon image](/static/mb/projects/stopwatch.png) This project turns the @boardname@ into a simple stopwatch. Pressing **A** starts the timer. Pressing **B** displays the elapsed seconds. -## Step 1 +## {Step 1} -Add an event to run code when ``||input:button A is pressed||``. +Use an event to run code when ``||input:button A is pressed||``. ```blocks input.onButtonPressed(Button.A, function () { }) ``` -## Step 2 +## {Step 2} Add code to store the current ``||input:running time||`` in a variable ``||variables:start||``. This is the start time. @@ -27,7 +27,7 @@ input.onButtonPressed(Button.A, function () { }) ``` -## Step 3 +## {Step 3} Add an event to run code when ``||input:button B is pressed||``. @@ -36,7 +36,7 @@ input.onButtonPressed(Button.B, function () { }) ``` -## Step 4 +## {Step 4} Add code to compute the difference between the ``||input:running time||`` and ``||variables:value||`` time. This is the elapsed millisecond since pressing button A. @@ -48,7 +48,7 @@ input.onButtonPressed(Button.B, function () { }) ``` -## Step 5 +## {Step 5} Add code to ``||basic:show||`` the number of milliseconds ``||variables:elapsed||``. Use ``||Math:integer division||`` to divide ``||variables:elapsed||`` by ``1000`` and get seconds. @@ -61,10 +61,14 @@ input.onButtonPressed(Button.B, function () { }) ``` -## Step 6 +## {Step 6} Try your program in the simulator. Press **A** to start the stopwatch and press **B** to get the current elapsed time. You can press **B** multiple times. -## Step 7 +## {Step 7} If you have a @boardname@ connected, click ``|Download|`` to transfer your code! + +```template +input.onButtonPressed(Button.A, function () {}) +``` diff --git a/docs/projects/tele-potato.md b/docs/projects/tele-potato.md index 317539e9508..b024d22ba39 100644 --- a/docs/projects/tele-potato.md +++ b/docs/projects/tele-potato.md @@ -55,7 +55,7 @@ To make the game less predictable, we use the ``||math:pick random||`` block to ```blocks let potato = 0 -input.onButtonPressed(Button.AB, () => { +input.onButtonPressed(Button.AB, function () { potato = randint(10, 20) }) ``` @@ -67,7 +67,7 @@ we have the potato and we can send it. After sending it, we set the **potato** v ```blocks let potato = 0 -input.onGesture(Gesture.Shake, () => { +input.onGesture(Gesture.Shake, function () { if (potato > 0) { radio.sendNumber(potato) potato = -1 @@ -97,7 +97,7 @@ Making the clock tick down is done with a ``||loops:forever||`` loop. ```blocks let potato = 0 -basic.forever(() => { +basic.forever(function () { if (potato == 0) { basic.showIcon(IconNames.Skull) } @@ -122,18 +122,18 @@ let potato = 0 radio.onReceivedNumber(function (receivedNumber) { potato = receivedNumber }) -input.onGesture(Gesture.Shake, () => { +input.onGesture(Gesture.Shake, function () { if (potato > 0) { radio.sendNumber(potato) potato = -1 } }) -input.onButtonPressed(Button.AB, () => { +input.onButtonPressed(Button.AB, function () { potato = randint(10, 20) }) radio.setGroup(1) potato = -1 -basic.forever(() => { +basic.forever(function () { if (potato == 0) { basic.showIcon(IconNames.Skull) } diff --git a/docs/projects/telegraph/code.md b/docs/projects/telegraph/code.md index 81cd617e872..8cb6556cc1d 100644 --- a/docs/projects/telegraph/code.md +++ b/docs/projects/telegraph/code.md @@ -54,7 +54,7 @@ Let's wrap it all in a forever loop so this code is running in the background al Modify your code to add the blocks below. Download the code onto one of the @boardname@s, press and release button **A** a few times. ```blocks -basic.forever(() => { +basic.forever(function () { if (input.buttonIsPressed(Button.A)) { pins.digitalWritePin(DigitalPin.P1, 1) led.plot(2, 2) @@ -78,18 +78,18 @@ We'll turn the LED in the bottom right corner (4, 4) on to show that we received Make sure your code looks like this: ```blocks -basic.forever(() => { +basic.forever(function () { if (input.buttonIsPressed(Button.A)) { - pins.digitalWritePin(DigitalPin.P1, 1); - led.plot(2, 2); + pins.digitalWritePin(DigitalPin.P1, 1) + led.plot(2, 2) } else { - pins.digitalWritePin(DigitalPin.P1, 0); + pins.digitalWritePin(DigitalPin.P1, 0) basic.clearScreen(); } if (pins.digitalReadPin(DigitalPin.P2) == 1) { - led.plot(4, 4); + led.plot(4, 4) } else { - led.unplot(4, 4); + led.unplot(4, 4) } }); ``` diff --git a/docs/projects/timing-gates.md b/docs/projects/timing-gates.md index cf694c1c19f..d1ad95bd407 100644 --- a/docs/projects/timing-gates.md +++ b/docs/projects/timing-gates.md @@ -12,12 +12,12 @@ Two gates are connected to the @boardname@ so it can detect a car passing throug ![](/static/mb/projects/timing-gates/sketchgates.jpg "Sketch of the gates") -As the car passes through the gate ``0``, it sends an event to the @boardname@ through the [``||pins:on pin pressed||``](/reference/input/on-pin-pressed) block. +As the car passes through the gate ``0``, it sends an event to the @boardname@ through the [``||input:on pin pressed||``](/reference/input/on-pin-pressed) block. The @boardname@ records the time in a variable ``t0``. ![](/static/mb/projects/timing-gates/sketchgate1.jpg "Sketch first gate") -As the car passes through the gate ``1``, it sends an event to the @boardname@ through the [``||pins:on pin pressed||``](/reference/input/on-pin-pressed) block. +As the car passes through the gate ``1``, it sends an event to the @boardname@ through the [``||input:on pin pressed||``](/reference/input/on-pin-pressed) block. The @boardname@ records the time in a variable ``t1``. ![](/static/mb/projects/timing-gates/sketchgate2.jpg "Sketch first gate") @@ -48,11 +48,11 @@ basic.showLeds(` . . . . . . . . . . `) -input.onPinPressed(TouchPin.P0, () => {}) +input.onPinPressed(TouchPin.P0, function () {}) let t = 0 input.runningTime() t - 1 -control.eventTimestamp(); +control.eventTimestamp() basic.showNumber(0) ``` @@ -87,7 +87,7 @@ The gate is ready to use! Your circuit should look like the picture below: ## Detecting the car with code -The @boardname@ provides an event [``||pins:on pin pressed||``](/reference/input/on-pin-pressed) +The @boardname@ provides an event [``||input:on pin pressed||``](/reference/input/on-pin-pressed) that is raised when a circuit between ``GND`` and a pin is detected. The circuit conductor could be a wire or even your body! We will attach a foil to the bottom of the car. When it passes over the gate, it connects both foil strips, closes the circuit and triggers the event. @@ -101,7 +101,7 @@ basic.showLeds(` . . . . . . . . . . `) -input.onPinPressed(TouchPin.P0, () => { +input.onPinPressed(TouchPin.P0, function () { basic.showLeds(` # . . . . # . . . . @@ -145,7 +145,7 @@ Connect the crocodile plugs to the ``GND`` and ``P1`` pins on the @boardname@. ## Detecting the second gate -Since the second gate is connected to pin ``P1``, we add a second [``||pins:on pin pressed||``](/reference/input/on-pin-pressed) event +Since the second gate is connected to pin ``P1``, we add a second [``||input:on pin pressed||``](/reference/input/on-pin-pressed) event that display 2 columns of LEDs. ```blocks @@ -156,7 +156,7 @@ basic.showLeds(` . . . . . . . . . . `) -input.onPinPressed(TouchPin.P0, () => { +input.onPinPressed(TouchPin.P0, function () { basic.showLeds(` # . . . . # . . . . @@ -165,7 +165,7 @@ input.onPinPressed(TouchPin.P0, () => { # . . . . `) }) -input.onPinPressed(TouchPin.P1, () => { +input.onPinPressed(TouchPin.P1, function () { basic.showLeds(` # . . . # # . . . # @@ -186,8 +186,8 @@ We will record the time where each gate is tripped in variables ``t0`` and ``t1` We take the different between ``t1`` and ``t0`` to compute the duration between the gates. ```blocks -let t0 = 0; -let t1 = 0; +let t0 = 0 +let t1 = 0 basic.showLeds(` . . . . . . . . . . @@ -195,8 +195,8 @@ basic.showLeds(` . . . . . . . . . . `) -input.onPinPressed(TouchPin.P0, () => { - t0 = control.eventTimestamp(); +input.onPinPressed(TouchPin.P0, function () { + t0 = control.eventTimestamp() basic.showLeds(` # . . . . # . . . . @@ -205,8 +205,8 @@ input.onPinPressed(TouchPin.P0, () => { # . . . . `) }) -input.onPinPressed(TouchPin.P1, () => { - t1 = control.eventTimestamp(); +input.onPinPressed(TouchPin.P1, function () { + t1 = control.eventTimestamp() basic.showLeds(` # . . . # # . . . # diff --git a/docs/projects/toys.md b/docs/projects/toys.md index 908ed5a9748..3962f261e7c 100644 --- a/docs/projects/toys.md +++ b/docs/projects/toys.md @@ -23,13 +23,22 @@ "url":"https://www.jasmineflorentine.com/ticklebot", "description": "A tickelish robot!", "imageUrl":"/static/mb/projects/ticklebot.jpg" +}, { + "name": "Octobot", + "url": "https://browndoggadgets.dozuki.com/Guide/Octobot/306", + "description": "Don't wake the Ocotobot!", + "imageUrl": "/static/mb/projects/octobot.jpg" +}, { + "name": "Two Player Maze", + "url": "https://tinker-club.blogspot.com/p/two-player-maze-game-for-microbit.html", + "description": "Build a metal ball maze for 2 players!", + "imageUrl": "/static/mb/projects/twoplayermaze.jpg" }, { "name": "Milky Monster", "url":"/projects/milky-monster", "description": "Make a funny milky-monster robot!", "imageUrl":"/static/mb/projects/milky-monster.jpg" -} -] +}] ``` ## Reusing toys diff --git a/docs/projects/tug-of-led.md b/docs/projects/tug-of-led.md index 976f7923345..25bf70987e1 100644 --- a/docs/projects/tug-of-led.md +++ b/docs/projects/tug-of-led.md @@ -7,7 +7,7 @@ Instead of a rope, we'll use the LED screen by pulling the LED light through the ![A micro:bit holding a rope](/static/mb/projects/tug-of-led.png) -## Step 1 +## {Step 1} Create a new variable ``||variables:rope||`` and put it in the ``||basic:on start||``. This will track the progress of the game. The ``||variables:rope||`` variable will be used as the **x** @@ -17,9 +17,9 @@ coordinate of the LED to lit so we set it to ``2`` to start. let rope = 2 ``` -## Step 2 +## {Step 2} -Add a ``||basic:forever||`` loop that turns on the LED at the ``||variables:rope||`` position. +In the ``||basic:forever||`` loop, put in a ``||basic:clear screen||`` and plot an LED at the ``||variables:rope||`` position. ```blocks let rope = 2 @@ -29,7 +29,7 @@ basic.forever(function() { }) ``` -## Step 3 +## {Step 3} Add an event on ``||input:button A pressed||`` to change the ``||variables:rope||`` value by **-0.1**. @@ -40,7 +40,7 @@ input.onButtonPressed(Button.A, function () { }) ``` -## Step 4 +## {Step 4} Add an event on ``||input:button B pressed||`` to change the ``||variables:rope||`` value by **0.1**. @@ -50,7 +50,7 @@ input.onButtonPressed(Button.B, function () { rope += 0.1 }) ``` -## Step 5 +## {Step 5} Because a button press pulls the rope by **0.1** in either direction, plot the ``||math:round||`` value of ``||variables:rope||`` to the nearest LED. @@ -62,7 +62,7 @@ basic.forever(function() { }) ``` -## Step 6 +## {Step 6} Back in the ``||basic:forever||``, add code to test ``||logic:if||`` the ``||variables:rope||`` is negative then ``||basic:show||`` **A WINS** on the screen. @@ -79,7 +79,7 @@ basic.forever(function() { }) ``` -## Step 7 +## {Step 7} Add an ``||logic:else if||`` condition to test ``||logic:if||`` the ``||variables:rope||`` is greater than 4 then ``||basic:show||`` **B WINS** on the screen. @@ -98,6 +98,6 @@ basic.forever(function() { }) ``` -## Step 8 +## {Step 8} Find a friend and start button smashing! diff --git a/docs/projects/turtle-scanner.md b/docs/projects/turtle-scanner.md index ccd6830a95d..75067ce4603 100644 --- a/docs/projects/turtle-scanner.md +++ b/docs/projects/turtle-scanner.md @@ -18,7 +18,7 @@ The turtle scans the display over and over again. turtle.setPosition(0, 0) turtle.turnRight() turtle.setSpeed(20) -basic.forever(() => { +basic.forever(function () { turtle.forward(4) turtle.turnRight() turtle.forward(1) diff --git a/docs/projects/turtle-spiral.md b/docs/projects/turtle-spiral.md index a845d7a7498..e5f85460ebb 100644 --- a/docs/projects/turtle-spiral.md +++ b/docs/projects/turtle-spiral.md @@ -17,7 +17,7 @@ A turtle that spirals into the center of the display and back out again. ```blocks turtle.setPosition(0, 0) turtle.turnRight() -basic.forever(() => { +basic.forever(function () { for (let index = 0; index <= 4; index++) { turtle.forward(4 - index) turtle.turnRight() diff --git a/docs/projects/turtle-square.md b/docs/projects/turtle-square.md index 9c5c0839acb..f9e3a76c71a 100644 --- a/docs/projects/turtle-square.md +++ b/docs/projects/turtle-square.md @@ -43,11 +43,11 @@ input.onButtonPressed(Button.A, function() { ## "for" is for repetition -Did you notice the pattern of repeated blocks needed to draw a square? Try using a ``for`` loop to achieve the same effect. +Did you notice the pattern of repeated blocks needed to draw a square? Try using a ``for`` loop with an `index` limit of **3** to achieve the same effect. ```blocks input.onButtonPressed(Button.A, function() { - for(let index = 0; index <= 4; index++) { + for(let index = 0; index <= 3; index++) { turtle.forward(1) turtle.turnRight() } @@ -61,13 +61,17 @@ The turtle holds a **pen** that can turn on LEDs. If you add the ``||turtle:pen| ```blocks input.onButtonPressed(Button.A, function() { turtle.pen(TurtlePenMode.Down) - for(let index = 0; index <= 4; index++) { + for(let index = 0; index <= 3; index++) { turtle.forward(1) turtle.turnRight() } }) ``` +```blockconfig.global +for(let index = 0; index <= 3; index++) {} +``` + ```package microturtle=github:Microsoft/pxt-microturtle#v0.0.9 ``` diff --git a/docs/projects/v2-blow-away.md b/docs/projects/v2-blow-away.md new file mode 100644 index 00000000000..c960d155695 --- /dev/null +++ b/docs/projects/v2-blow-away.md @@ -0,0 +1,261 @@ +# Blow Away + +## {Introduction pt. 1 @unplugged} + +đŸ‘ģ Oh, no! Your @boardname@ is being haunted by a ghost named Haven đŸ‘ģ + +For this tutorial, we'll learn how to blow Haven away đŸŒŦī¸ + +![Blow away banner message](/static/mb/projects/blow-away.png) + +## {Haunted ghost setup} + +A wild Haven has appeared! + +■ From the ``||basic:Basic||`` category, find ``||basic:show icon [ ]||`` and add it to your ``||basic:on start||`` container. +■ Click the heart icon and set it to show a ghost. +💡 In the ``show icon`` dropdown menu options, you can hover to see what each design is called. + +```blocks +// @highlight +basic.showIcon(IconNames.Ghost) +``` + +--- + +## {Loop setup} + +■ From the ``||loops:Loops||`` category, find the ``||loops:repeat [4] times||`` loop and snap it into your empty ``||basic(noclick):forever||`` container. +💡 Why do we need a [__*repeat loop*__](#repeatLoop "repeat code for a given number of times") when we already have a ``forever`` container? Because ``forever`` has an embedded delay that we want to avoid! + +```blocks +basic.forever(function () { + // @highlight + for (let index = 0; index < 4; index++) { + + } +}) +``` + +## {Conditional setup} + +Haven hates noise and will blow away if things get too loud. Let's use an [__*if statement*__](#ifstatement "if this condition is met, do something") to check for sounds. + +■ From ``||logic:Logic||``, grab an ``||logic:if then||`` statement and snap it into your empty ``||loops(noclick):repeat [4] times do||`` loop. +■ Go back to ``||logic:Logic||`` to get a ``||logic:<[0] [=] [0]>||`` comparison. +■ Snap ``||logic:<[0] [=] [0]>||`` in to **replace** the ``||logic(noclick):||`` condition for your ``||logic(noclick):if then||`` statement. + +```blocks +basic.forever(function () { + // @highlight + for (let index = 0; index < 4; index++) { + // @highlight + if (0 == 0) { + + } + } +}) +``` + +## {Blow sound} + +We'll be using a [__*sound threshold*__](#soundThreshold "a number for how loud a sound needs to be to trigger an event. 0 = silence to 255 = maximum noise") to act as Haven's ears. + +■ From the ``||input:Input||`` category, drag ``||input:sound level||`` in to **replace** the **_left_ ``0``** of your ``||logic(noclick):<[0] [=] [0]>||`` comparison. +■ Using the dropdown in the **middle** of ``||logic(noclick):[sound level] [=] [0]||``, change the comparison to be **``>``** (greater than). +■ Finally, have the **right side** of the comparison say ``128`` so your full comparison reads: **``sound level > 128``**. +💡 This means Haven will hear any sound above ``128``. + +```blocks +basic.forever(function () { + for (let index = 0; index < 4; index++) { + // @highlight + if (input.soundLevel() > 128) { + + } + } +}) +``` + +## {Making variables} + +Let's create some [__*variables*__](#variable "a holder for information that may change") to keep track of Haven's movement. + +■ In the ``||variables:Variables||`` category, click on ``Make a Variable...`` and make a variable named ``col``. +💡 ``col`` is short for "column". +■ Make **another** variable and name it ``row``. + +## {Displacing LEDs part 1} + +To show Haven is blowing away, we want to move a random set of lights sideways. + +■ Your ``||variables:Variables||`` category should now have the option to ``||variables:set [row] to [0]||``. Drag that block into your empty ``||logic(noclick):if then||`` statement. +■ From the ``||math:Math||`` category, find ``||math:pick random [0] to [10]||`` and snap that in to **replace** the ``[0]`` in your ``||variables(noclick):set [row] to [0]||`` block. +■ Change the maximum number from ``10`` to **``4``**. +💡 We are setting the maximum random value to 4 because the lights on the @boardname@ are numbered 0, 1, 2, 3, and 4 for columns and rows. + +```blocks +let row = 0 +basic.forever(function () { + for (let index = 0; index < 4; index++) { + if (input.soundLevel() > 128) { + // @highlight + row = randint(0, 4) + } + } +}) +``` + +## {Displacing LEDs part 2} + +■ Go back into ``||variables:Variables||`` and drag out another ``||variables:set [row] to [0]||``. Place this one below the last one (at **the end**) of your `if then` statement. +■ Using the **dropdown menu**, set the new block to read ``||variables(noclick):set [col] to [0]||``. +■ From the ``||math:Math||`` category, grab another ``||math:pick random [0] to [10]||`` and snap that in to **replace** the ``[0]`` in your ``||variables(noclick):set [col] to [0]||`` block. +■ Change the maximum number from ``10`` to **``4``**. + +```blocks +let col = 0 +let row = 0 +basic.forever(function () { + for (let index = 0; index < 4; index++) { + if (input.soundLevel() > 128) { + row = randint(0, 4) + // @highlight + col = randint(0, 4) + } + } +}) +``` + +## {Conditioning on one point} + +Time to move some lights around! + +■ From ``||logic:Logic||``, grab another ``||logic:if then||`` and snap it at the **inside and at the bottom of** your ``||loops(noclick):repeat [4] times do||`` loop, right below your ``||logic(noclick):if [sound level] [>] [128]||`` statement. +■ From the ``||led:Led||`` category, find ``||led:point x [0] y [0]||`` and drag it in to **replace** the ``||logic(noclick):||`` condition in the **new** ``||logic(noclick):if then||`` statement. +💡 This block will test if the light is on at the the given ``x`` and ``y`` coordinate points. + +```blocks +let col = 0 +let row = 0 +basic.forever(function () { + for (let index = 0; index < 4; index++) { + if (input.soundLevel() > 128) { + row = randint(0, 4) + col = randint(0, 4) + } + // @highlight + if (led.point(0, 0)) { } + } +}) +``` + +## {Unplotting and replotting LEDs} + +To create the animation effect of Haven blowing away, we will turn off (or ``unplot``) a light that is on and then turn it on again (``plot`` it) in a different spot. + +■ From ``||led:Led||``, grab ``||led:unplot x [0] y [0]||`` and snap it inside the **empty** ``||logic(noclick):if then||`` statement. +■ Go back to ``||led:Led||`` and get ``||led:plot x [0] y [0]||``. Snap that in **beneath** the ``||led(noclick):unplot x [0] y [0]||`` block that you just added. + +```blocks +let col = 0 +let row = 0 +basic.forever(function () { + for (let index = 0; index < 4; index++) { + if (input.soundLevel() > 128) { + row = randint(0, 4) + col = randint(0, 4) + } + // @highlight + if (led.point(0, 0)) { + led.unplot(0, 0) + led.plot(0, 0) + } + } +}) +``` + +## Setting variables + +Notice how you have **three** blocks from the ``||led:Led||`` category. All three have ``||led(noclick):x||`` ``[0]`` and ``||led(noclick):y||`` ``[0]`` coordinates. In these **two** steps, we will set it so that every ``||led(noclick):x||`` is followed by the ``||variables:col||`` variable and every ``||led(noclick):y||`` is followed by the ``||variables(noclick):row||`` variable. +■ From ``||variables:Variables||``, get three copies of ``||variables:col||``, and use them to **replace the ``x`` values** in the following three blocks: +**1.** ``||led(noclick):point x [0] y [0]||`` +**2.** ``||led(noclick):unplot x [0] y [0]||`` +**3.** ``||led(noclick):plot x [0] y [0]||`` +■ Go into ``||variables:Variables||``, get three copies of ``||variables:row||``, and use them to **replace the ``y`` values** in the same three blocks. + +```blocks +let col = 0 +let row = 0 +basic.forever(function () { + for (let index = 0; index < 4; index++) { + if (input.soundLevel() > 128) { + row = randint(0, 4) + col = randint(0, 4) + } + // @highlight + if (led.point(col, row)) { + led.unplot(col, row) + led.plot(col, row) + } + } +}) +``` + +## {Moving LEDs} + +Right now, we are unplotting and replotting in the same spot. What we want to do is move the lights we're turning back on just a smidge to the right every time until there's nothing left on the grid. + +■ From ``||math:Math||``, find the ``||math:[0] [+] [0]||`` operation and use it to **replace** ``||variables(noclick):col||`` in your ``||led(noclick):plot x [col] y [row]||`` block. +💡 If you move your entire ``||basic(noclick):forever||`` container, you should find a greyed out ``col`` variable in your workspace. +■ Take the greyed out ``||variables(noclick):col||`` variable (or get a new one) and use it to **replace** the **_first_ ``[0]``** so the operation reads ``||math(noclick):[col] [+] [0]||``. +■ Replace the **_second_ ``[0]``** with **``[1]``** so the operation reads ``||math(noclick):[col] [+] [1]||``. + +```blocks +let col = 0 +let row = 0 +basic.forever(function () { + for (let index = 0; index < 4; index++) { + if (input.soundLevel() > 128) { + row = randint(0, 4) + col = randint(0, 4) + } + // @highlight + if (led.point(col, row)) { + led.unplot(col, row) + led.plot(col + 1, row) + } + } +}) +``` + +## {Testing in the simulator} + +Check out the simulator! + +■ Click on the pink bar underneath the microphone icon. Drag it above the sound number you chose (we used ``128``!) to blow Haven away. +■ If you have a new @boardname@ (the one with the **shiny gold** logo at the top), download this code and try it out! +💡 Blow close to the @boardname@ and watch Haven swoosh away 💨 +💡 Use your @boardname@'s reset button (it's on the back!) to bring Haven back đŸ‘ģ + +```blocks +let col = 0 +let row = 0 +basic.showIcon(IconNames.Ghost) +basic.forever(function () { + for (let index = 0; index < 4; index++) { + if (input.soundLevel() > 128) { + row = randint(0, 4) + col = randint(0, 4) + } + if (led.point(col, row)) { + led.unplot(col, row) + led.plot(col + 1, row) + } + } +}) +``` + +```validation.global +# BlocksExistValidator +``` \ No newline at end of file diff --git a/docs/projects/v2-cat-napping.md b/docs/projects/v2-cat-napping.md new file mode 100644 index 00000000000..1ffbf3c6565 --- /dev/null +++ b/docs/projects/v2-cat-napping.md @@ -0,0 +1,202 @@ +# Cat Napping + +## {Introduction @unplugged} + +Lychee the cat loves the sun and wants to know if your home has a good sunbathing spot. Are you up for the challenge? + +![Cat Tanning banner message, an image of a cat](/static/mb/projects/cat-napping/1_lychee.png) + +## {Setting logging to false on start} + +First, we want to make sure we know when our micro:bit is collecting data. To do this, let's create a [__*boolean*__](#boolean "something that is only true or false") [__*variable*__](#variable "a holder for information that may change") and use it to track when the @boardname@ is logging data. We'll start with the logging variable set to false. + +■ In the ``||variables:Variables||`` category, click on ``Make a Variable...`` and make a variable named ``logging``. +■ From the ``||variables:Variables||`` category, grab the ``||variables:set [logging] to [0]||`` block and snap it into the empty ``||basic(noclick):on start||`` container. +■ From the ``||logic:Logic||`` category, grab a ``||logic:||`` argument and snap it in to **replace** the ``||variables(noclick):[0]||`` value in your ``||variables(noclick):set [logging] to [0]||`` statement. + +```blocks +let logging = false +logging = false +``` + +## {Toggle logging on A press} + +Let's give Lychee some control over when she wants to start and stop logging data on the @boardname@. + +■ From the ``||input:Input||`` category, grab a ``||input:on button [A] pressed||`` container and drag it into your workspace. Then, grab a ``||variables:set [logging] to [0]||`` block from ``||variables:Varables||`` and snap it inside of your ``||input(noclick):on button [A] pressed||`` container. +■ From the ``||logic:Logic||`` category, grab a ``||logic:||`` argument and snap it in to **replace** the ``0`` argument. Go back to the ``||variables:Variables||`` category, grab a ``||variables:logging||`` variable and snap it in to **replace** the empty ``||logic(noclick):<>||`` in the ``||logic(noclick):not <>||`` statement. + +✋🛑 Take a moment to help Lychee answer the following question: _What is happening every time she presses the A button?_ + +```blocks +let logging = false +input.onButtonPressed(Button.A, function () { + logging = !(logging) +}) +``` + +## {Visual logging indicators} + +It would help to know when the @boardname@ is logging data and when it isn't. For this step, we will be building out a visual indicator using an [__*if then / else*__](#ifthenelse "runs some code if a boolean condition is true and different code if the condition is false") statement. + +■ From the ``||logic:Logic||`` category, grab an ``||logic:if then / else||`` statement and snap it in at the **bottom** of your ``||input(noclick):on button [A] pressed||`` container. +■ From ``||variables:Variables||``, grab a ``||variables:logging||`` variable and snap it in to **replace** the ``||logic(noclick):||`` condition in your ``||logic(noclick):if then / else||`` statement. + +```blocks +let logging = false +input.onButtonPressed(Button.A, function () { + logging = !(logging) + if (logging) { + } else { + } +}) +``` + +## {Set the indicator icon} + +■ Let's display an image when the @boardname@ is logging data. From the ``||basic:Basic||`` category, grab a ``||basic:show icon [ ]||`` block and snap it into the empty **top container** of your ``||logic(noclick):if then / else||`` statement. +■ Set it to show the "target" icon (it looks like an empty sun - scroll down to find it!). This will show whenever your @boardname@ is collecting data. +💡 In the ``show icon`` dropdown menu options, you can hover to see what each design is called. + +```blocks +let logging = false +input.onButtonPressed(Button.A, function () { + logging = !(logging) + if (logging) { + basic.showIcon(IconNames.Target) + } else { + } +}) +``` + +## {Auditory logging indicators} + +Let's now add an auditory indicator that your @boardname@ is logging data! + +■ From the ``||music:Music||`` category, grab a ``||music:play sound [dadadum] [in background]||`` block and snap it into the **bottom** of the **top container** of your ``||logic(noclick):if then / else||`` statement. +■ Click on the ``[dadadum]`` dropdown and select ``nyan``, then set the playback mode to ``||music(noclick):[until done]||``. Your block should now say ``||music(noclick):play melody [nyan] [until done]||``. + +```blocks +let logging = false +input.onButtonPressed(Button.A, function () { + logging = !(logging) + if (logging) { + basic.showIcon(IconNames.Target) + music.play(music.builtInPlayableMelody(Melodies.Nyan), music.PlaybackMode.UntilDone) + } else { + } +}) +``` + +## {Logging off indicator} + +■ Let's clear the board when the @boardname@ is not logging data. From the ``||basic:Basic||`` category, grab a ``||basic:clear screen||`` block and snap it into the empty **bottom container** of your ``||logic(noclick):if then / else||`` statement. + +```blocks +let logging = false +input.onButtonPressed(Button.A, function () { + logging = !(logging) + if (logging) { + basic.showIcon(IconNames.Target) + music.play(music.builtInPlayableMelody(Melodies.Nyan), music.PlaybackMode.UntilDone) + } else { + basic.clearScreen() + } +}) +``` + +## {Time interval for data logging} + +Let's set up the data logging for Lychee! In order to get Lychee a good amount of data without running out of memory, we should collect one data point for her every minute. + +■ From the ``||loops:Loops||`` category, grab a ``||loops:every [500] ms||`` container and add it to your workspace. +■ Click on the the ``500`` dropdown and select ``1 minute``.
+💡 1 minute is equivalent to 60000ms, which is what the number will automatically change to. + +```blocks +loops.everyInterval(60000, function () { +}) +``` + +## {Setting up a logging variable} + +Now, let's use an [__*if then*__](#ifthen "runs some code if a boolean condition is true") statement to track when the @boardname@ is logging data. + +■ From the ``||logic:Logic||`` category, grab a ``||logic:if then||`` statement and snap it into your ``||loops(noclick):every [600000] ms||`` container. +■ From the ``||variables:Variables||`` category, drag out a ``||variables:logging||`` variable and snap it in to **replace** the ``||logic(noclick):||`` argument in the ``||logic(noclick):if then||`` statement. + +```blocks +let logging = false +loops.everyInterval(60000, function () { + if (logging) { + } +}) +``` + +## {Setting up logging - Part 1} + +Lychee loves her sun spots because they provide a nice, sunny and warm place to nap. So, we'll need to measure the **temperature** and **light** in different places around the house. + +■ From the ``||datalogger:Data Logger||`` category, grab a ``||datalogger:log data [column [""] value [0]] +||`` block and snap it **inside** the ``||logic(noclick):if [logging] then||`` statement. +■ Click on the ``""`` after the word ``column`` and type in "``temp``". +■ From the ``||input:Input||`` category, select the ``||input:temperature (°C)||`` parameter and drag it in to **replace** the ``0`` after the word ``value``. + +```blocks +let logging = false +loops.everyInterval(60000, function () { + if (logging) { + //@highlight + datalogger.log( + datalogger.createCV("temp", input.temperature()) + ) + } +}) +``` + +## {Setting up logging - Part 2} + +■ On the right of the ``||input(noclick):temperature (°C)||`` input that you just snapped in, there is a ➕ button. Click on it. You should now see a new row that says ``||datalogger(noclick):column [""] value [0]||``. +■ Click on the empty ``""`` after the word ``column`` and type in "``light``". +■ From the ``||input:Input||`` category, select the ``||input:light level||`` parameter and drag it in to **replace** the ``0`` parameter after the word ``value``. + +```blocks +let logging = false +loops.everyInterval(60000, function () { + if (logging) { + //@highlight + datalogger.log( + datalogger.createCV("temp", input.temperature()), + datalogger.createCV("light", input.lightLevel()) + ) + } +}) +``` + +## {Time to log data! @unplugged} + +You did it! If you have a @boardname@ V2 (the one with the **shiny gold** logo at the top), download this code and try it out! + +■ Find a sun spot in your house and press the ``A`` button to start logging data - your display should show an icon and play a sound to indicate that you are logging data. +■ After some time (we recommend at least an hour), press the ``A`` button again to stop logging data - your display should clear to indicate that you are not logging data. + +## {Reviewing your data @unplugged} + +Now that you have logged some data, plug your @boardname@ into a laptop or desktop computer. The @boardname@ will appear like a USB drive called MICROBIT. Look in there and you'll see a file called MY_DATA: + +![MY_DATA file highlighted in file folder](/static/mb/projects/cat-napping/11_mydata.png) + +Double-click on MY_DATA to open it in a web browser and you'll see a table with your data: + +![Image of sample data file](/static/mb/projects/cat-napping/11_datafile.png) + +## {Lychee's preferences @unplugged} + +Does your home have a good sunbathing spot for Lychee? Compare the light and temperature levels you record for different areas around your house! The sunniest and warmest spots will likely be her favorite â˜€ī¸đŸ˜ģ + +```template +// +``` + +```package +datalogger +``` diff --git a/docs/projects/v2-clap-lights.md b/docs/projects/v2-clap-lights.md new file mode 100644 index 00000000000..e3c697e3508 --- /dev/null +++ b/docs/projects/v2-clap-lights.md @@ -0,0 +1,170 @@ +# Clap Lights + +## {Introduction @unplugged} + +The new @boardname@s have a microphone to help them detect sound 🎤 + +Let's learn how to use a clap 👏 to switch your @boardname@'s lights on and off! + +![Clap lights banner message](/static/mb/projects/clap-lights.png) + +## {Setting up the sound input} + +■ From the ``||input:Input||`` category, find the ``||input:on [loud] sound||`` container and add it to your workspace. + +```blocks +// @highlight +input.onSound(DetectedSound.Loud, function () { + +}) +``` + +## {Creating a lightsOn variable} + +Let's begin by creating a [__*variable*__](#variable "a holder for information that may change") to keep track of whether the @boardname@'s lights are on or off. + +■ In the ``||variables:Variables||`` category, click on ``Make a Variable...`` and make a variable named ``lightsOn``. + +## {Displaying LEDs part 1} + +In this step, we'll be using an [__*if then / else*__](#ifthenelse "runs some code if a Boolean condition is true and different code if the condition is false") statement. + +■ From the ``||logic:Logic||`` category, grab an ``||logic:if then / else||`` block and snap it into your ``||input(noclick):on [loud] sound||`` container. +■ Look in the ``||variables:Variables||`` category. Find the new ``||variables:lightsOn||`` variable and snap it in to **replace** the ``||logic(noclick):||`` value in your ``||logic(noclick):if then / else||`` statement. + +```blocks +let lightsOn = 0 +input.onSound(DetectedSound.Loud, function () { + // @highlight + if (lightsOn) { + + } else { + + } +}) +``` + +## {Displaying LEDs part 2} + +■ From ``||basic:Basic||``, grab ``||basic:show leds||`` and snap it into the **top container** of your ``||logic(noclick):if then / else||`` statement. +■ Set the lights to a pattern you like! +💡 In the hint, we chose to turn on all of the outside lights. Feel free to make your own design 🎨 + +```blocks +let lightsOn = 0 +input.onSound(DetectedSound.Loud, function () { + if (lightsOn) { + // @highlight + basic.showLeds(` + # # # # # + # . . . # + # . . . # + # . . . # + # # # # # + `) + } else { + } +}) +``` + +## {Clearing the screen} + +■ From ``||basic:Basic||``, find ``||basic:clear screen||`` and snap it into the **bottom container** of your ``||logic(noclick):if then / else||`` section. +💡 This will turn the display off if ``lightsOn`` is **not** ``true``. + +```blocks +let lightsOn = 0 +input.onSound(DetectedSound.Loud, function () { + if (lightsOn) { + basic.showLeds(` + # # # # # + # . . . # + # . . . # + # . . . # + # # # # # + `) + } else { + // @highlight + basic.clearScreen() + } +}) +``` + +## {Setting the lightsOn variable} + +Just like we'd toggle a light switch, each time we clap, we want to **flip** the variable ``lightsOn`` to the **opposite** of what it was before. + +■ From ``||variables:Variables||``, locate ``||variables:set [lightsOn] to [0]||`` and snap it in at the **very top** of your ``||input(noclick):on [loud] sound||`` container. +■ From the ``||logic:Logic||`` category, find the ``||logic:not <>||`` operator and use it to **replace the ``[0]``** in ``||variables(noclick):set [lightsOn] to [0]||``. +■ From ``||variables:Variables||``, grab ``||variables:lightsOn||`` and snap it into the **empty part** of the ``||logic(noclick):not <>||`` operator. + +```blocks +let lightsOn = false +input.onSound(DetectedSound.Loud, function () { + // @highlight + lightsOn = !(lightsOn) + if (lightsOn) { + basic.showLeds(` + # # # # # + # . . . # + # . . . # + # . . . # + # # # # # + `) + } else { + basic.clearScreen() + } +}) +``` + +## {Testing in the simulator} + +■ Check out the simulator! +■ Click on the pink slider bar beneath the microphone icon and drag it up and down. +💡 Right now, your @boardname@ thinks that anything above 128 is loud. Every time the sound goes > 128, your lights should switch on/off. + +## {Set loud sound threshold} + +Your @boardname@ might detect sounds when you don't want it to. Setting a [__*sound threshold*__](#soundThreshold "a number for how loud a sound needs to be to trigger an event. 0 = silence to 255 = maximum noise") could help 🔉🔊 + +■ Click on the ``||input:Input||`` category. A new category should show up beneath it called ``||input:...more||``. +■ From ``||input:...more||``, grab ``||input:set [loud] sound threshold to [128]||`` and snap it into your **empty** ``||basic(noclick):on start||`` container. +💡 Try to change the value of your sound threshold so that every time you clap, your lights will turn on if they are off and vice versa. + +```blocks +// @highlight +input.setSoundThreshold(SoundThreshold.Loud, 128) +``` + +## {Testing, round 2} + +Don't forget to test your code in the simulator! + +If you have a new @boardname@ (the one with the **shiny gold** logo at the top), download this code and try it out! + +```blocks +let lightsOn = false +input.onSound(DetectedSound.Loud, function () { + lightsOn = !(lightsOn) + if (lightsOn) { + basic.showLeds(` + # # # # # + # . . . # + # . . . # + # . . . # + # # # # # + `) + } else { + basic.clearScreen() + } +}) +input.setSoundThreshold(SoundThreshold.Loud, 128) +``` + +```validation.global +# BlocksExistValidator +``` + +```template +// +``` \ No newline at end of file diff --git a/docs/projects/v2-countdown.md b/docs/projects/v2-countdown.md new file mode 100644 index 00000000000..ca25706ea48 --- /dev/null +++ b/docs/projects/v2-countdown.md @@ -0,0 +1,127 @@ +# Countdown + +## {Introduction @unplugged} + +🎇3...🎇2...🎇1... +🎆GO!🎆 + +Let's create a musical countdown using the new @boardname@ with sound! + +![Countdown banner message](/static/mb/projects/countdown.png) + +## {Setting up the loop} + +We'll begin by using a [__*for loop*__](#forLoop "repeat code for a given number of times using an index") to recreate the same sound 3 times. + +■ From the ``||loops:Loops||`` category in your toolbox, find the ``||loops:for [index] from 0 to [4]||`` loop and add it to your ``||basic(noclick):on start||`` container. +■ Change your loop to count from ``0`` to **``2``**. +💡 This means the loop will count 0-1-2 instead of what we want, which is 3-2-1. We will worry about this later! + +```blocks +// @highlight +for (let index = 0; index <= 2; index++) { + +} +``` + +## {Play music} + +■ From ``||music:Music||``, grab ``||music:play tone [Middle C] for [1 beat] [until done]||`` and snap it into your empty ``for`` loop. +💡 Your simulator might start playing music. You can mute it if distracting. +■ 1 beat is a little long. Use the **dropdown** to set the tone to play for ``||music(noclick):1/4 beat||``. + +```blocks +for (let index = 0; index <= 2; index++) { + // @highlight + music.play(music.tonePlayable(262, music.beat(BeatFraction.Quarter)), music.PlaybackMode.UntilDone) +} +``` + +## {Showing a number} + +With every tone, we also want to **display** our countdown. + +■ From ``||basic:Basic||``, find ``||basic:show number [0]||`` and snap it in at the **bottom** of your ``for`` loop. +■ From your ``||loops(noclick):for [index] from 0 to [2]||`` loop condition, click and drag out the **red** ``||variables(noclick):index||`` variable. +■ Use the ``||variables(noclick):index||`` that you dragged out to **replace** the ``0`` in ``||basic(noclick):show number [0]||``. + +```blocks +for (let index = 0; index <= 2; index++) { + music.play(music.tonePlayable(262, music.beat(BeatFraction.Quarter)), music.PlaybackMode.UntilDone) + // @highlight + basic.showNumber(index) +} +``` + +## {Inverting the number} + +If you take a look at your simulator, you'll notice the @boardname@ flashing 0-1-2. We want it to say 3-2-1! Let's learn a trick to change that. + +■ From the ``||math:Math||`` category, snap ``||math:[0] - [0]||`` in to **replace** ``||variables(noclick):index||`` in your ``||basic(noclick):show number [index]||`` block. +💡 You should now have a greyed out ``index`` variable in your workspace. We'll use that in the next step. +■ Pick up the greyed out ``||variables(noclick):index||`` variable and snap it in to the **right side** of your ``||math:[0] - [0]||`` operator. +💡 Can't find ``||variables(noclick):index||``? Try moving your ``||basic(noclick):on start||`` container to see if ``||variables(noclick):index||`` is hiding behind it! +■ Set the **left side** of your ``||math(noclick):[0]-[index]||`` operator to **``3``**. +💡 Why does this work? Every time we loop, our ``index`` variable will grow by 1 and our @boardname@ will output: 3-0 = **3** âžĄī¸ 3-1 = **2** âžĄī¸ 3-2 = **1**! + +```blocks +for (let index = 0; index <= 2; index++) { + music.play(music.tonePlayable(262, music.beat(BeatFraction.Quarter)), music.PlaybackMode.UntilDone) + // @highlight + basic.showNumber(3 - index) +} +``` + +## {Printing "GO!"} + +■ From ``||basic:Basic||``, grab ``||basic:show string ["Hello!"]||`` and snap it into the **very bottom** of your ``||basic(noclick):on start||`` container. +■ Replace ``Hello!`` with the word ``GO!`` + +```blocks +for (let index = 0; index <= 2; index++) { + music.play(music.tonePlayable(262, music.beat(BeatFraction.Quarter)), music.PlaybackMode.UntilDone) + basic.showNumber(3 - index) +} +// @highlight +basic.showString("GO!") +``` + +## {Adding a "GO!" noise} + +■ From the ``||music:Music||`` category, grab ``||music:play tone [Middle C] for [1 beat] [until done]||`` and place it **above** your ``||basic(noclick):show string ["GO!"]||`` block and **below** your ``||loops(noclick):for||`` loop. +💡 This will let your @boardname@ play the sound and show ``GO!`` at the same time. +■ Set the ``||music(noclick):tone||`` to be ``Middle G``. +💡 ``Middle G`` is also tone ``392``. + +```blocks +for (let index = 0; index <= 2; index++) { + music.play(music.tonePlayable(262, music.beat(BeatFraction.Quarter)), music.PlaybackMode.UntilDone) + basic.showNumber(3 - index) +} +// @highlight +music.play(music.tonePlayable(392, music.beat(BeatFraction.Whole)), music.PlaybackMode.UntilDone) +basic.showString("GO!") +``` + +## {Testing in the simulator} + +Make sure your speakers are on and check out the simulator! + +If you have a @boardname@ with sound (the one with the **shiny gold** logo at the top), no need to plug in an external speaker - just download this code and try it out! + +```blocks +for (let index = 0; index <= 2; index++) { + music.play(music.tonePlayable(262, music.beat(BeatFraction.Quarter)), music.PlaybackMode.UntilDone) + basic.showNumber(3 - index) +} +music.play(music.tonePlayable(392, music.beat(BeatFraction.Whole)), music.PlaybackMode.UntilDone) +basic.showString("GO!") +``` + +```validation.global +# BlocksExistValidator +``` + +```template +// +``` \ No newline at end of file diff --git a/docs/projects/v2-morse-chat.md b/docs/projects/v2-morse-chat.md new file mode 100644 index 00000000000..6725b39494c --- /dev/null +++ b/docs/projects/v2-morse-chat.md @@ -0,0 +1,315 @@ +# Morse Chat + +## {Introducing Sky @unplugged} + +🐷 Meet Sky, the pig! Sky can only communicate using __*Morse code*__. + +Luckily, you can use your @boardname@ with sound to talk to Sky 👋 + +![Morse chat banner message](/static/mb/projects/morse-chat.png) + +## {Setup} + +Let's start by making a way to send Morse code messages. + +~hint What is Morse code? 🤷đŸŊ + +--- + +Morse code is an alphabet composed of dots (short signals) and dashes (long signals). The message +**"Hi there!"** is **".... .. - .... . .-. . -.-.--"** in Morse code. + +hint~ + +■ From the ``||radio:Radio||`` category, get a ``||radio:radio set group [1]||`` and drop it into your empty ``||basic:on start||`` container. You can leave the group ID at `1` or change it to something different (the radio group ID is like a channel number to talk on). +■ From the ``||input:Input||`` category in the toolbox, drag an ``||input:on logo [pressed]||`` container into to your workspace. +■ From the ``||radio:Radio||`` category, get ``||radio:radio send number [0]||`` and snap it into your empty ``||input:on logo [pressed]||`` container. + +```blocks +radio.setGroup(1) +input.onLogoEvent(TouchButtonEvent.Pressed, function () { + radio.sendNumber(0) +}) +``` + +## {Sending different messages pt. 1} + +■ From ``||input:Input||``, grab **another** ``||input:on logo [pressed]||`` container and add it to your workspace. +💡 This container is greyed out because it matches another. Let's change that! +■ On the greyed-out ``||input(noclick):on logo [pressed]||`` container, click on the **``pressed``** dropdown and set it to ``||input(noclick):long pressed||``. + +```blocks +radio.setGroup(1) +// @highlight +input.onLogoEvent(TouchButtonEvent.LongPressed, function () { +}) +input.onLogoEvent(TouchButtonEvent.Pressed, function () { + radio.sendNumber(0) +}) +``` + +## {Sending different messages pt. 2} + +■ From the ``||radio:Radio||`` category, get a ``||radio:radio send number [0]||`` block and snap it into your **empty** ``||input(noclick):on logo [long pressed]||`` container. +■ Set the number to be ``1``. + +```blocks +radio.setGroup(1) +input.onLogoEvent(TouchButtonEvent.LongPressed, function () { + // @highlight + radio.sendNumber(1) +}) +input.onLogoEvent(TouchButtonEvent.Pressed, function () { + radio.sendNumber(0) +}) +``` + +## {Receiving different messages} + +To ensure Sky gets the right message, we will use an [__*if then / else*__](#ifthenelse "runs some code if a boolean condition is true and different code if the condition is false") conditional statement. + +■ From ``||radio:Radio||``, find the ``||radio:on radio received [receivedNumber]||`` container and add it to your workspace. +■ From ``||logic:Logic||``, grab an ``||logic:if then / else||`` statement and snap it into your **new** ``||radio(noclick):on radio received [receivedNumber]||`` container. +■ Go back to the ``||logic:Logic||`` category, grab ``||logic:<[0] [=] [0]>||``, and click it in to **replace** the ``||logic(noclick):||`` argument in your ``||logic(noclick):if then / else||`` statement. + +```blocks +radio.onReceivedNumber(function (receivedNumber) { + // @highlight + if (0 == 0) { + + } else { + + } +}) +``` + +## {Conditioning on the input} + +■ From your ``||radio:on radio received [receivedNumber]||`` container, grab the **``receivedNumber``** input and drag out a copy. +■ Use your copy of **``receivedNumber``** to replace the ``[0]`` on the **left side** of ``||logic(noclick):<[0] [=] [0]>||``. + +```blocks +radio.onReceivedNumber(function (receivedNumber) { + // @highlight + if (receivedNumber == 0) { + + } else { + + } +}) +``` + +## {Displaying a message pt. 1} + +■ We want to display a dash if the logo is long pressed. From ``||basic:Basic||``, grab ``||basic:show leds||`` and snap it into the empty **bottom container** of your ``||logic(noclick):if then / else||`` statement. +■ Turn on 3 LEDs in a row to be a dash: - + +```blocks +radio.onReceivedNumber(function (receivedNumber) { + if (receivedNumber == 0) { + } else { + // @highlight + basic.showLeds(` + . . . . . + . . . . . + . # # # . + . . . . . + . . . . . + `) + } +}) +``` + +## {Playing a sound pt. 1} + +■ From the ``||music:Music||`` category, grab a ``||music:play tone [Middle C] for [1 beat] [until done]||`` block and snap it at the **end** of the **bottom container** in your ``||logic(noclick):if then / else||`` statement. + +```blocks +radio.onReceivedNumber(function (receivedNumber) { + if (receivedNumber == 0) { + } else { + basic.showLeds(` + . . . . . + . . . . . + . # # # . + . . . . . + . . . . . + `) + // @highlight + music.play(music.tonePlayable(262, music.beat(BeatFraction.Whole)), music.PlaybackMode.UntilDone) + } +}) +``` + +## {Displaying a message pt. 2} + +■ We want to display a dot if the logo is pressed. From ``||basic:Basic||``, grab another ``||basic:show leds||`` and snap it into the **top container** of your ``||logic(noclick):if then / else||`` statement. +■ Turn on a single LED to make a dot: . + +```blocks +radio.onReceivedNumber(function (receivedNumber) { + if (receivedNumber == 0) { + // @highlight + basic.showLeds(` + . . . . . + . . . . . + . . # . . + . . . . . + . . . . . + `) + } else { + basic.showLeds(` + . . . . . + . . . . . + . # # # . + . . . . . + . . . . . + `) + music.play(music.tonePlayable(262, music.beat(BeatFraction.Whole)), music.PlaybackMode.UntilDone) + } +}) +``` + +## {Playing a sound pt. 2} + +■ From the ``||music:Music||`` category, grab ``||music:play tone [Middle C] for [1 beat] [until done]||`` and snap it in at the **end** of the **top container** in your ``||logic(noclick):if then / else||`` statement. +■ Dots are shorter than dashes! Set the tone to play for ``1/4 beat``. + +```blocks +radio.onReceivedNumber(function (receivedNumber) { + if (receivedNumber == 0) { + basic.showLeds(` + . . . . . + . . . . . + . . # . . + . . . . . + . . . . . + `) + // @highlight + music.play(music.tonePlayable(262, music.beat(BeatFraction.Quarter)), music.PlaybackMode.UntilDone) + } else { + basic.showLeds(` + . . . . . + . . . . . + . # # # . + . . . . . + . . . . . + `) + music.play(music.tonePlayable(262, music.beat(BeatFraction.Whole)), music.PlaybackMode.UntilDone) + } +}) +``` + +## {Clearing the screens} + +■ From ``||basic:Basic||``, find ``||basic:clear screen||`` and snap it in at the **very bottom** of your ``||radio(noclick):on radio received [receivedNumber]||`` container. + +```blocks +radio.onReceivedNumber(function (receivedNumber) { + if (receivedNumber == 0) { + basic.showLeds(` + . . . . . + . . . . . + . . # . . + . . . . . + . . . . . + `) + music.play(music.tonePlayable(262, music.beat(BeatFraction.Quarter)), music.PlaybackMode.UntilDone) + + } else { + basic.showLeds(` + . . . . . + . . . . . + . # # # . + . . . . . + . . . . . + `) + music.play(music.tonePlayable(262, music.beat(BeatFraction.Whole)), music.PlaybackMode.UntilDone) + + } + // @highlight + basic.clearScreen() +}) +``` + +## {Testing in the simulator - Connect!} + +Test what you've created. Remember to turn your sound on! + +■ Touch the gold **micro:bit logo** at the top of your @boardname@ on the simulator. You'll notice that a second @boardname@ appears. This is the @boardname@ for Sky 🐖 +💡 If your screen is too small, you might not be able to see it. + +```blocks +radio.setGroup(1) +radio.onReceivedNumber(function (receivedNumber) { + if (receivedNumber == 0) { + basic.showLeds(` + . . . . . + . . . . . + . . # . . + . . . . . + . . . . . + `) + music.play(music.tonePlayable(262, music.beat(BeatFraction.Quarter)), music.PlaybackMode.UntilDone) + + } else { + basic.showLeds(` + . . . . . + . . . . . + . # # # . + . . . . . + . . . . . + `) + music.play(music.tonePlayable(262, music.beat(BeatFraction.Whole)), music.PlaybackMode.UntilDone) + + } + basic.clearScreen() +}) +input.onLogoEvent(TouchButtonEvent.LongPressed, function () { + radio.sendNumber(1) +}) +input.onLogoEvent(TouchButtonEvent.Pressed, function () { + radio.sendNumber(0) +}) +``` + +## {Testing in the simulator - Send message} + +■ Touch the logo again to send messages to Sky 🐖 +**Press** to send a dot. +**Long press** (count to 3!) to send a dash. +■ If you have multiple @boardname@s with sound (they have **shiny gold** logos at the top), download this code and try it out! + +```blocks +radio.setGroup(1) +radio.onReceivedNumber(function (receivedNumber) { + if (receivedNumber == 0) { + basic.showLeds(` + . . . . . + . . . . . + . . # . . + . . . . . + . . . . . + `) + music.play(music.tonePlayable(262, music.beat(BeatFraction.Quarter)), music.PlaybackMode.UntilDone) + + } else { + basic.showLeds(` + . . . . . + . . . . . + . # # # . + . . . . . + . . . . . + `) + music.play(music.tonePlayable(262, music.beat(BeatFraction.Whole)), music.PlaybackMode.UntilDone) + + } + basic.clearScreen() +}) +input.onLogoEvent(TouchButtonEvent.LongPressed, function () { + radio.sendNumber(1) +}) +input.onLogoEvent(TouchButtonEvent.Pressed, function () { + radio.sendNumber(0) +}) +``` diff --git a/docs/projects/v2-pet-hamster.md b/docs/projects/v2-pet-hamster.md new file mode 100644 index 00000000000..d653218f7fb --- /dev/null +++ b/docs/projects/v2-pet-hamster.md @@ -0,0 +1,144 @@ +# Pet Hamster + +## {Introduction @unplugged} + +![Pet hamster banner message](/static/mb/projects/pet-hamster.png) + +## {Cyrus's asleep face} + +Cyrus is a very sleepy hamster. In fact, Cyrus is almost always sleeping. + +■ From the ``||basic:Basic||`` category, find ``||basic:show icon [ ]||`` and snap it into your ``||basic:on start||`` container. Set it to show the asleep ``-_-`` face. +💡 In the ``show icon`` dropdown menu options, you can hover to see what each design is called! + +```blocks +//@highlight +basic.showIcon(IconNames.Asleep) +``` + +## {Giggly Cyrus} + +Pressing Cyrus's logo tickles them! + +■ From ``||input:Input||``, find the ``||input:on logo [pressed]||`` container and drag it into your workspace. +■ Go to ``||basic:Basic||`` and grab **another** ``||basic:show icon [ ]||``. Snap it into your **empty** ``||input(noclick):on logo [pressed]||`` container. Set the icon (Cyrus's face) to happy ``:)``. + +```blocks +//@highlight +input.onLogoEvent(TouchButtonEvent.Pressed, function () { + //@highlight + basic.showIcon(IconNames.Happy) +}) +``` + +## {Tickle sound} + +■ From the ``||music:Music||`` category, get a ``||music:play [melody jump up] [in background]||`` and add it to the **bottom** of your ``||input(noclick):on logo [pressed]||`` container. Change the playback mode to ``||music(noclick):[until done]||``. + +```blocks +input.onLogoEvent(TouchButtonEvent.Pressed, function () { + basic.showIcon(IconNames.Happy) + //@highlight + music._playDefaultBackground(music.builtInPlayableMelody(Melodies.JumpUp), music.PlaybackMode.UntilDone) +}) +``` + +## {Dizzy Cyrus} + +Whenever Cyrus is shaken, they get sad 🙁 + +■ From ``||input:Input||``, find ``||input:on [shake]||`` and drag it into your workspace. +■ From the ``||basic:Basic||`` category, grab ``||basic:show icon [ ]||`` and snap it into your **new** ``||input(noclick):on [shake]||`` container. Set the icon (Cyrus's face) to sad ``:(``. + +```blocks +//@highlight +input.onGesture(Gesture.Shake, function () { + //@highlight + basic.showIcon(IconNames.Sad) +}) +``` + +## {Dizzy sound} + +■ From the ``||music:Music||`` category, find the ``||music:play [melody dadadum] [in background]||`` block and add it to the **bottom** of your ``||input(noclick):on [shake]||`` container. Change the playback mode to ``||music(noclick):[until done]||``. +■ Click on the **dropdown** and set it so Cyrus plays a sad sound until done. + +```blocks +input.onGesture(Gesture.Shake, function () { + basic.showIcon(IconNames.Sad) + //@highlight + music._playDefaultBackground(music.builtInPlayableMelody(Melodies.Wawawawaa), music.PlaybackMode.UntilDone) +}) +``` + +## {Cyrus's default face pt. 1} + +Let's ensure that Cyrus will always go back to sleep after being shaken or tickled. + +■ Right click the ``||basic(noclick):show icon[-_-]||`` block in your workspace (inside the ``||basic(noclick):on start||`` container) and choose **Duplicate**. +■ Snap your copied block in at the **very bottom** of your ``||input(noclick):on [shake]||`` container. + +```blocks +input.onGesture(Gesture.Shake, function () { + basic.showIcon(IconNames.Sad) + music._playDefaultBackground(music.builtInPlayableMelody(Melodies.Wawawawaa), music.PlaybackMode.UntilDone) + //@highlight + basic.showIcon(IconNames.Asleep) +}) +input.onLogoEvent(TouchButtonEvent.Pressed, function () { + basic.showIcon(IconNames.Happy) + music._playDefaultBackground(music.builtInPlayableMelody(Melodies.JumpUp), music.PlaybackMode.UntilDone) +}) +basic.showIcon(IconNames.Asleep) +``` + +## {Cyrus's default face pt. 2} + +■ Duplicate the ``||basic(noclick):show icon[-_-]||`` block again and this time snap it in at the **very bottom** of your ``||input(noclick):on logo [pressed]||`` container. + +```blocks +input.onGesture(Gesture.Shake, function () { + basic.showIcon(IconNames.Sad) + music._playDefaultBackground(music.builtInPlayableMelody(Melodies.Wawawawaa), music.PlaybackMode.UntilDone) + basic.showIcon(IconNames.Asleep) +}) +input.onLogoEvent(TouchButtonEvent.Pressed, function () { + basic.showIcon(IconNames.Happy) + music._playDefaultBackground(music.builtInPlayableMelody(Melodies.JumpUp), music.PlaybackMode.UntilDone) + //@highlight + basic.showIcon(IconNames.Asleep) +}) +basic.showIcon(IconNames.Asleep) +``` + +## {Testing in the simulator} + +Check out the simulator and make sure your speakers are on 🔊 + +Play with Cyrus to see how they react 🐹 +**Click on the SHAKE button** to shake Cyrus. +**Touch the gold logo at the top** to tickle Cyrus. + +If you have a new @boardname@ (the one with the **shiny gold** logo at the top), download this code and try it out! + +```blocks +input.onGesture(Gesture.Shake, function () { + basic.showIcon(IconNames.Sad) + music._playDefaultBackground(music.builtInPlayableMelody(Melodies.Wawawawaa), music.PlaybackMode.UntilDone) + basic.showIcon(IconNames.Asleep) +}) +input.onLogoEvent(TouchButtonEvent.Pressed, function () { + basic.showIcon(IconNames.Happy) + music._playDefaultBackground(music.builtInPlayableMelody(Melodies.JumpUp), music.PlaybackMode.UntilDone) + basic.showIcon(IconNames.Asleep) +}) +basic.showIcon(IconNames.Asleep) +``` + +```validation.global +# BlocksExistValidator +``` + +```template +// +``` \ No newline at end of file diff --git a/docs/projects/v2-play-sound-long.md b/docs/projects/v2-play-sound-long.md new file mode 100644 index 00000000000..777c2d91445 --- /dev/null +++ b/docs/projects/v2-play-sound-long.md @@ -0,0 +1,144 @@ +# Dance to the Beat + +## 1. Introduction @unplugged + +The new micro:bits have speakers, which leaves you free to move around in ways you weren't able to before! + +Let's use movement to create a beat box of your own. + +![Dance beat banner message](/static/mb/projects/dance-beat.png) + + +## 2. Understanding Input + +Let's find out what numbers the micro:bit produces when you move it around. + +--- + +â‡ŧ Open the ``||serial:^ Advanced||`` category to show the ``||serial:Serial||`` label. + +â‡ŧ From ``||serial:Serial||``, drag the ``||serial:serial write value ["x"] = [0]||`` +block into the ``||basic(noclick):forever||`` loop container. + + + +```blocks +basic.forever(function(){ + serial.writeValue("x", 0) +}) +``` + + + +## 3. See the Console + +When your code runs again, you'll see a button below the micro:bit that says +"Show console Simulator". + +Click that button to see what happens. + +--- + +The graph for the simulator should stay flat at 0 +and the text in the console below should say "x:0". + + + +## 4. Acceleration Values + +For the graph to change with the speed of your movement, we need to replace the "0" +with the micro:bit **acceleration** value. + +--- + +â‡ŧ Open the ``||input:Input||`` category and drag ``||input:acceleration (mg) [x]||`` +over to replace "0" in the ``||serial(noclick):serial write value ["x"] = [0]||`` +block. + +â‡ŧ Change "x" to "a" (for "acceleration".) + + + +```blocks +basic.forever(function(){ + serial.writeValue("a", input.acceleration(Dimension.X)) +}) +``` + + + +## 5. Look Again + +Click the "Show console Simulator" button again. + +--- + +Now you should see your graph and text change between **-1023** and **1023** as you click +around on the micro:bit simulator to pretend like you're swinging it around. + + + +## 6. Compass Values + +For the graph to change with the speed of your movement, we need to replace the "0" with +the micro:bit **acceleration** value. + +--- + +â‡ŧ Open the ``||input:Input||`` category and drag ``||input:acceleration (mg) [x]||`` +over to replace "0" in the ``||serial(noclick):serial write value ["x"] = [0]||`` +block. + +â‡ŧ Change "x" to "a" (for "acceleration".) + + + +```blocks +basic.forever(function(){ + serial.writeValue("a", input.acceleration(Dimension.X)) +}) +``` + +## Finale + +👏 **YOU DID IT!** 👏 + +Don't forget to test your code in the simulator! + +If you have a new @boardname@ (the one with the **shiny gold** logo at the top), download this code and try it out! + +```blocks +basic.forever(function () { + serial.writeValue("Accel", input.acceleration(Dimension.X)) + serial.writeValue("Compass", input.compassHeading()) + music.playSoundEffect(music.createSoundEffect( + WaveShape.Sine, + input.acceleration(Dimension.X), + input.compassHeading(), + 255, + 0, + 500, + SoundExpressionEffect.None, + InterpolationCurve.Linear + ), SoundExpressionPlayMode.UntilDone) +}) +``` + +```ghost + +basic.forever(function () { + serial.writeValue("Accel", input.acceleration(Dimension.X)) + serial.writeValue("Compass", input.compassHeading()) + music.playSoundEffect(music.createSoundEffect( + WaveShape.Sine, + input.acceleration(Dimension.X), + input.compassHeading(), + 255, + 0, + 500, + SoundExpressionEffect.None, + InterpolationCurve.Linear + ), SoundExpressionPlayMode.UntilDone) +}) + +``` \ No newline at end of file diff --git a/docs/projects/v2-play-sound.md b/docs/projects/v2-play-sound.md new file mode 100644 index 00000000000..7220aed0c72 --- /dev/null +++ b/docs/projects/v2-play-sound.md @@ -0,0 +1,167 @@ +# Dance to the Beat + +## Introduction @unplugged + +The new micro:bit has speakers so you can hear sounds without being tied down! + +Let's use movement to create an electronic beat of our own. + +![Dance beat banner message](/static/mb/projects/dance-beat.png) + + +## Add Play Sound Block + +To start your electronic beat, you'll want to repeat a sound forever. + +--- + +â‡ŧ Open the ``||music:Music||`` category and drag the ``||music:play sound [â™Ģ âˆŋâˆŋâˆŋâˆŋ +] [until done]||`` +block into the empty ``||basic(noclick):forever||`` loop container. + + + +```blocks +basic.forever(function(){ + music.playSoundEffect(music.createSoundEffect(WaveShape.Sine, 5000, 0, 255, 0, 500, SoundExpressionEffect.None, InterpolationCurve.Linear), SoundExpressionPlayMode.UntilDone) +}) +``` + + + +## Listen Close + +When your code runs again, you should hear a short laser/alarm +sound that repeats over and over forever. + +--- + +💡 _You can press the stop button (_âšī¸_) beneath the simulator to prevent the noise from bothering you while you're coding._ + + + + + +## Make a Change + +For the sound to change with your speed of movement, we need to put the +micro:bit **acceleration** value in the sound block. + +--- + +â‡ŧ On the ``||music(noclick):play sound [â™Ģ âˆŋâˆŋâˆŋ +] [until done]||`` block, click the plus icon (**+**) +to show the start frequency value of 5000. + +â‡ŧ From the ``||input:Input||`` category, drag the ``||input:acceleration (mg) [x]||`` +value block to replace **5000**. + + +```blocks +basic.forever(function(){ + music.playSoundEffect(music.createSoundEffect(WaveShape.Sine, input.acceleration(Dimension.X), 0, 255, 0, 500, SoundExpressionEffect.None, InterpolationCurve.Linear), SoundExpressionPlayMode.UntilDone) +}) +``` + + + +## Listen Again + +Run your code again. + +--- + +This time, hover around above the micro:bit to simulate swinging it from side to side. +You should hear the sound change as the micro:bit moves. + + + +## Rotation Values + +You can make the beat even more fun by changing the end frequency as the micro:bit rotates. + +--- + +â‡ŧ From the ``||input:...more||`` category, drag the ``||input:rotation (°) [pitch]||`` +value block to replace the frequency **0**. + + +```blocks +basic.forever(function(){ + music.playSoundEffect(music.createSoundEffect(WaveShape.Sine, input.acceleration(Dimension.X), input.rotation(Rotation.Pitch), 255, 0, 500, SoundExpressionEffect.None, InterpolationCurve.Linear), SoundExpressionPlayMode.UntilDone) +}) +``` + + + +## Listen Again + +Run your code again. + + +This time, roll over the micro:bit in all directions to simulate turning it while you +swing it around. + +--- + +You should hear the sound change from high to low and low to high. + +Try moving the micro:bit in different ways. Can you make a fun beat? + + + +## Customize Your Beat + +Try changing the **duration** of the beat to something other than **500**.
+Then, change the dropdown selection inside both the **acceleration** and **rotation** +blocks. + +What else can you click on to edit your sound? + + + + +## Finale + +👏 **YOU DID IT!** 👏 + +Imagine how exciting this project will be when it's loaded on a micro:bit! + +If you have a micro:bit v2 (the one with the **shiny gold** logo at the top), +download this code and hold your micro:bit while you dance. + +Congratulations, you are your own DJ! + + + +```blocks +basic.forever(function () { + music.playSoundEffect(music.createSoundEffect( + WaveShape.Sine, + input.acceleration(Dimension.X), + input.rotation(Rotation.Pitch), + 255, + 0, + 500, + SoundExpressionEffect.None, + InterpolationCurve.Linear + ), SoundExpressionPlayMode.UntilDone) +}) +``` + +```ghost + +basic.forever(function () { + serial.writeValue("Accel", input.acceleration(Dimension.X)) + serial.writeValue("Rotation", input.rotation(Rotation.Pitch),) + music.playSoundEffect(music.createSoundEffect( + WaveShape.Sine, + input.acceleration(Dimension.X), + input.rotation(Rotation.Pitch), + 255, + 0, + 500, + SoundExpressionEffect.None, + InterpolationCurve.Linear + ), SoundExpressionPlayMode.UntilDone) +}) + +``` \ No newline at end of file diff --git a/docs/projects/voting-machine.md b/docs/projects/voting-machine.md index d47b400a452..682a4c52b69 100644 --- a/docs/projects/voting-machine.md +++ b/docs/projects/voting-machine.md @@ -21,7 +21,7 @@ Assuming button ``A`` is for a NO vote and ``B`` is for YES, the voter program w When button ``A`` is pressed, a number ``0`` is sent via radio and the ``X`` symbol is shown on the screen. ```block -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { radio.sendNumber(0) basic.showIcon(IconNames.No) }) @@ -32,7 +32,7 @@ input.onButtonPressed(Button.A, () => { When button ``B`` is pressed, a number ``255`` is sent via radio and the ``Y`` symbol is shown on the screen. ```block -input.onButtonPressed(Button.B, () => { +input.onButtonPressed(Button.B, function () { radio.sendNumber(255) basic.showIcon(IconNames.Yes) }) @@ -56,11 +56,11 @@ radio.setGroup(4) Putting all the parts together, here's the complete voter program: ```blocks -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { radio.sendNumber(0) basic.showIcon(IconNames.No) }) -input.onButtonPressed(Button.B, () => { +input.onButtonPressed(Button.B, function () { radio.sendNumber(255) basic.showIcon(IconNames.Yes) }) diff --git a/docs/projects/wallet.md b/docs/projects/wallet.md index ec238af0bfe..9d43a6fdfab 100644 --- a/docs/projects/wallet.md +++ b/docs/projects/wallet.md @@ -21,10 +21,6 @@ Let's get started! ## ~ -## Flipgrid - -https://flipgrid.com/c616f092 - ## References The wallet built in this activity is inspired from the duct tape wallet in diff --git a/docs/projects/wallet/code.md b/docs/projects/wallet/code.md index 9a4790e0706..d4c526faf2b 100644 --- a/docs/projects/wallet/code.md +++ b/docs/projects/wallet/code.md @@ -5,7 +5,7 @@ Let's start by using a combination of [forever](/reference/basic/forever) and [show leds](/reference/basic/show-leds) to create animation: ```blocks -basic.forever(() => { +basic.forever(function () { basic.showLeds(` # # . # # # # . # # @@ -34,7 +34,7 @@ How do we know that the wallet is in the pocket? It is really dark in there... W Using an [if statement](/blocks/logic/if), we can test if the level of light is sufficient to turn on the screen. Otherwise, we turn off the screen for a few second to save energy. ```blocks -basic.forever(() => { +basic.forever(function () { if (input.lightLevel() > 16) { basic.showLeds(` # # . # # diff --git a/docs/projects/watch.md b/docs/projects/watch.md index 52377d634f0..7a87ccf5f52 100644 --- a/docs/projects/watch.md +++ b/docs/projects/watch.md @@ -29,10 +29,6 @@ Let's get started! ## ~ -## Flipgrid - -https://flipgrid.com/0398a822 - ## Additional coding activities * [Countdown timer](/projects/watch/timer) diff --git a/docs/projects/watch/code.md b/docs/projects/watch/code.md index 41e4c718761..1ce89cfd2ad 100644 --- a/docs/projects/watch/code.md +++ b/docs/projects/watch/code.md @@ -17,9 +17,9 @@ We need a variable to keep track of how many motions you make. 3. Let's show that there are no motions counted yet. Get a ``||basic:show number||`` from **Basic** and put it after the variable. Now, change the `0` to the `motions` variable from the **Variables** category in the toolbox. ```blocks -let motions = 0; -motions = 0; -basic.showNumber(motions); +let motions = 0 +motions = 0 +basic.showNumber(motions) ``` ## Count your movements @@ -31,10 +31,10 @@ Ok, now we'll count and show all of your movements. 3. Grab another ``||basic:show number||`` and put it at the bottom of the ``||input:on shake||``. Find `motions` again back over in **Variables** and replace the `0` with it. ```blocks -let motions = 0; -input.onGesture(Gesture.Shake, () => { - motions += 1; - basic.showNumber(motions); +let motions = 0 +input.onGesture(Gesture.Shake, function () { + motions += 1 + basic.showNumber(motions) }) ``` @@ -46,10 +46,10 @@ If we want to start over from zero, then we need to have a way to reset the moti 2. Grab another ``||basic:show number||`` and change the `0` to the a `motions` variable. ```blocks -let motions = 0; -input.onButtonPressed(Button.A, () => { - motions = 0; - basic.showNumber(motions); +let motions = 0 +input.onButtonPressed(Button.A, function () { + motions = 0 + basic.showNumber(motions) }) ``` diff --git a/docs/projects/watch/digital-watch.md b/docs/projects/watch/digital-watch.md index b05d96615d1..ff227f8e5fc 100644 --- a/docs/projects/watch/digital-watch.md +++ b/docs/projects/watch/digital-watch.md @@ -39,9 +39,9 @@ So, let's try showing the time on the display. We aren't keeping time yet but we let time = "" let minutes = 0 let hours = 0 -input.onGesture(Gesture.Shake, () => { - time = hours + (":" + minutes); - basic.showString(time); +input.onGesture(Gesture.Shake, function () { + time = hours + (":" + minutes) + basic.showString(time) }) ``` ## Set the time with buttons @@ -58,12 +58,12 @@ Let's make a way to set the hours for the watch. 6. In the ``||logic:else||`` section, put a ``||variables:set to||`` there. Select the `hours` variable name from the dropdown and leave the `0`. ```blocks -let hours = 0; -input.onButtonPressed(Button.A, () => { +let hours = 0 +input.onButtonPressed(Button.A, function () { if (hours < 23) { - hours += 1; + hours += 1 } else { - hours = 0; + hours = 0 } }) ``` @@ -76,12 +76,12 @@ Setting minutes is almost the same as setting hours but with just a few changes. 3. Change every variable name from `hours` to `minutes`. Change the `23` in the ``||logic:if||`` condition to ``59``. This is the limit of minutes we count. ```blocks -let minutes = 0; -input.onButtonPressed(Button.B, () => { +let minutes = 0 +input.onButtonPressed(Button.B, function () { if (minutes < 59) { - minutes += 1; + minutes += 1 } else { - minutes = 0; + minutes = 0 } }) ``` @@ -95,9 +95,9 @@ Time is shown in either 24 hour or 12 hour format. We'll use one more button to 3. Pick up a `ampm` from **Variables** and connect it on the right of the ``||logic:not||``. This switches our 24 hour format to 12 hour and back. ```blocks -let ampm = false; -input.onButtonPressed(Button.AB, () => { - ampm = !(ampm); +let ampm = false +input.onButtonPressed(Button.AB, function () { + ampm = !(ampm) }) ``` @@ -114,12 +114,12 @@ A watch really has three parts: the display, settings, and timer. We need a way ```blocks let minutes = 0; -basic.forever(() => { +basic.forever(function () { basic.pause(60000) if (minutes < 59) { - minutes += 1; + minutes += 1 } else { - minutes = 0; + minutes = 0 } }) ``` @@ -134,7 +134,7 @@ basic.forever(() => { ```blocks let minutes = 0 let hours = 0 -basic.forever(() => { +basic.forever(function () { basic.pause(60000) if (minutes < 59) { minutes += 1 @@ -167,7 +167,7 @@ First, we have to code an adjustment for the hours number when we're using the 1 let hours = 0; let adjust = 0; let ampm = false; -input.onGesture(Gesture.Shake, () => { +input.onGesture(Gesture.Shake, function () { adjust = hours; if (ampm) { if (hours > 12) { @@ -194,24 +194,24 @@ Now, we have to join up the hours and minutes to make text that will display on 7. In the fourth copy, change the first `""` in the ``||text:join||`` to the variable `time`. Change the second string in the ``||text:join||`` to a ``minutes``. ```blocks -let minutes = 0; -let hours = 0; -let adjust = 0; -let time = ""; -let ampm = false; -input.onGesture(Gesture.Shake, () => { - adjust = hours; +let minutes = 0 +let hours = 0 +let adjust = 0 +let time = "" +let ampm = false +input.onGesture(Gesture.Shake, function () { + adjust = hours if (ampm) { if (hours > 12) { - adjust = hours - 12; + adjust = hours - 12 } else { if (hours == 0) { - adjust = 12; + adjust = 12 } } } - time = "" + adjust; - time = time + ":"; + time = "" + adjust + time = time + ":" if (minutes < 10) { time = time + "0" } @@ -230,12 +230,12 @@ Ok, we're getting close to finishing now. Here we need to add the 'AM' or 'PM' i 5. Finally, at the very bottom of ``||input:on shake||``, go get a ``||basic:show string||`` from **Basic** and put it there. Change the string `"Hello!"` to the `time` variable. ```blocks -let minutes = 0; -let hours = 0; -let adjust = 0; -let time = ""; -let ampm = false; -input.onGesture(Gesture.Shake, () => { +let minutes = 0 +let hours = 0 +let adjust = 0 +let time = "" +let ampm = false +input.onGesture(Gesture.Shake, function () { adjust = hours; if (ampm) { if (hours > 12) { @@ -246,7 +246,7 @@ input.onGesture(Gesture.Shake, () => { } } } - time = "" + adjust; + time = "" + adjust time = time + ":" if (minutes < 10) { diff --git a/docs/projects/watch/timer.md b/docs/projects/watch/timer.md index c63e91f9a40..bb5642c09bc 100644 --- a/docs/projects/watch/timer.md +++ b/docs/projects/watch/timer.md @@ -35,7 +35,7 @@ We'll use button `A` to add `10` seconds to our time count. The time count of `s ```blocks let seconds = 0; -input.onButtonPressed(Button.A, () => { +input.onButtonPressed(Button.A, function () { if (seconds < 50) { seconds += 10; basic.showNumber(seconds) @@ -57,9 +57,9 @@ Now, we'll use the `B` button to add just `1` second the time count. The time co ```blocks let seconds = 0; -input.onButtonPressed(Button.B, () => { +input.onButtonPressed(Button.B, function () { if (seconds < 60) { - seconds += 1; + seconds += 1 basic.showNumber(seconds) basic.clearScreen() } @@ -71,13 +71,13 @@ input.onButtonPressed(Button.B, () => { Ok, now we'll get the timer going and show how many seconds are left. This will happen when the watch is shaken! 1. Get an ``||input:on shake||`` block and place it in the workspace. -2. Pull out a ``||loops:while||`` from **Loops** and put it in the ``||input:on shake||``. Replace the `true` condition with the ``||logic:0 < 0||`` conditon from **Logic**. Make the `<` go to `>`. Change the `0` on the left to the `seconds` variable. +2. Pull out a ``||loops:while||`` from **Loops** and put it in the ``||input:on shake||``. Replace the `true` condition with the ``||logic:0 < 0||`` condition from **Logic**. Make the `<` go to `>`. Change the `0` on the left to the `seconds` variable. 3. Take out another ``||basic:show number||`` and put it inside the ``||loops:while||``. Change the `0` to the `seconds` variable. Put a ``||basic:pause||`` under that and set the time to `1000` milliseconds. This means our timer will count down by **1000** milliseconds, which is actually one second, each time through the loop. -4. To change the number of seconds left, get a ``||variables:change by||`` and place it below the ``||loops:pause||``. Find the ``||math:0 - 0||`` block in **Math** and put it in the ``||variables:change by||``. Set the `0` on the right of the minus to be a `1`. +4. To change the number of seconds left, get a ``||variables:change by||`` and place it below the ``||basic:pause||``. Find the ``||math:0 - 0||`` block in **Math** and put it in the ``||variables:change by||``. Set the `0` on the right of the minus to be a `1`. ```blocks let seconds = 0; -input.onGesture(Gesture.Shake, () => { +input.onGesture(Gesture.Shake, function () { while (seconds > 0) { basic.showNumber(seconds); basic.pause(1000); @@ -91,11 +91,11 @@ Add a few ``||basic:show icon||`` blocks at the bottom of the ``||loops:while||` ```blocks let seconds = 0; -input.onGesture(Gesture.Shake, () => { +input.onGesture(Gesture.Shake, function () { while (seconds > 0) { - basic.showNumber(seconds); - basic.pause(1000); - seconds -= 1; + basic.showNumber(seconds) + basic.pause(1000) + seconds -= 1 } basic.showIcon(IconNames.Diamond) basic.showIcon(IconNames.SmallDiamond) diff --git a/docs/reference.md b/docs/reference.md index 53abe589b58..e5be8f48fec 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -11,6 +11,7 @@ music.playTone(0, 0); led.plot(0, 0); radio.sendNumber(0); ``` + ## Advanced ```namespaces @@ -33,15 +34,13 @@ control.inBackground(() => { ```namespaces bluetooth.onBluetoothConnected(() => {}); -devices.tellCameraTo(MesCameraEvent.TakePhoto); ``` ```package radio -devices bluetooth ``` ## See Also -[basic](/reference/basic), [input](/reference/input), [music](/reference/music), [led](/reference/led), [Math (blocks)](/blocks/math), [String](/types/string), [game](/reference/game), [images](/reference/images), [pins](/reference/pins), [serial](/reference/serial), [control](/reference/control), [radio](/reference/radio), [devices](/reference/devices), [bluetooth](/reference/bluetooth) +[basic](/reference/basic), [input](/reference/input), [music](/reference/music), [led](/reference/led), [Math (blocks)](/blocks/math), [String](/types/string), [game](/reference/game), [images](/reference/images), [pins](/reference/pins), [serial](/reference/serial), [control](/reference/control), [radio](/reference/radio), [bluetooth](/reference/bluetooth) diff --git a/docs/reference/basic.md b/docs/reference/basic.md index 1f00a4fd9c0..fc4a74517f0 100644 --- a/docs/reference/basic.md +++ b/docs/reference/basic.md @@ -27,4 +27,4 @@ basic.showArrow(ArrowNames.North); [showIcon](/reference/basic/show-icon), [showLeds](/reference/basic/show-leds), [showString](/reference/basic/show-string), [clearScreen](/reference/basic/clear-screen), [forever](/reference/basic/forever), [pause](/reference/basic/pause), -[showArrow](/reference/basic/show-arrow), [showAnimation](/reference/basic/show-animation) +[showArrow](/reference/basic/show-arrow) \ No newline at end of file diff --git a/docs/reference/basic/forever.md b/docs/reference/basic/forever.md index c7f9bfb6d21..609439fd8e1 100644 --- a/docs/reference/basic/forever.md +++ b/docs/reference/basic/forever.md @@ -1,4 +1,4 @@ -# Forever +# forever Keep running part of a program [in the background](/reference/control/in-background). @@ -8,7 +8,20 @@ basic.forever(() => { }) ``` -## Example: compass +You can have part of a program continuously by placing it in an **forever** loop. The **forever** loop will _yield_ to the other code in your program though, allowing that code to have time to run when needs to. + +### ~ reminder + +#### Event-based loops + +Both the **forever** loop and the **every** loop are _event-based_ loops where the code inside is run as part of a function. These are different from the [for](/blocks/loops/for) and [while](/blocks/loops/while) loops. Those are loops are part of the programming language and can have [break](/blocks/loops/break) and [continue](/blocks/loops/continue) statements in them. +You can NOT use **break** or **continue** in either a **forever** loop or an **every** loop. + +### ~ + +## Examples + +### Example: compass The following example constantly checks the [compass heading](/reference/input/compass-heading) @@ -32,7 +45,7 @@ basic.forever(() => { }) ``` -## Example: counter +### Example: counter The following example keeps showing the [number](/types/number) stored in a global variable. When you press button `A`, the number gets bigger. @@ -66,5 +79,5 @@ input.onButtonPressed(Button.A, () => { ## See also -[while](/blocks/loops/while), [on button pressed](/reference/input/on-button-pressed), [in background](/reference/control/in-background) +[while](/blocks/loops/while), [in background](/reference/control/in-background), [every](/reference/loops/every-interval) diff --git a/docs/reference/basic/pause.md b/docs/reference/basic/pause.md index 4c962a73d96..8c72b19306b 100644 --- a/docs/reference/basic/pause.md +++ b/docs/reference/basic/pause.md @@ -1,6 +1,6 @@ # Pause -Pause the program for the number of milliseconds you say. +Pause the program for a duration of the number milliseconds you say. You can use this function to slow your program down. ```sig @@ -9,21 +9,24 @@ basic.pause(400) ## Parameters -* ``ms`` is the number of milliseconds that you want to pause (100 milliseconds = 1/10 second, and 1000 milliseconds = 1 second). +* **ms**: the number of milliseconds (duration) of your pause time. To convert from seconds: 100 milliseconds = 1/10 second and 1000 milliseconds = 1 second. -## Example: diagonal line +## Example -This example draws a diagonal line by turning on LED `0, 0` (top left) through LED `4, 4` (bottom right). -The program pauses 500 milliseconds after turning on each LED. -Without `pause`, the program would run so fast that you would not have time to see each LED turning on. +Randomly turn on and off the LED pixels on the screen. ```blocks -for (let i = 0; i < 5; i++) { - led.plot(i, i) - basic.pause(500) -} +let duration = 500 +basic.forever(function () { + led.toggle(randint(0, 4), randint(0, 4)) + basic.pause(duration) +}) ``` +## Advanced + +If **ms** is an invalid number (`NaN`, not a number), the pause will default to `20` ms. + ## See also [while](/blocks/loops/while), [running time](/reference/input/running-time), [for](/blocks/loops/for) diff --git a/docs/reference/basic/plot-leds.md b/docs/reference/basic/plot-leds.md deleted file mode 100644 index db6b2649a72..00000000000 --- a/docs/reference/basic/plot-leds.md +++ /dev/null @@ -1,34 +0,0 @@ -# Plot LEDs - -Display an [Image](/reference/images/image) on the @boardname@'s [LED screen](/device/screen). - -```sig -basic.showLeds(` -. . . . . -. # . # . -. . # . . -# . . . # -. # # # . -`) -``` - -## Parameters - -* leds - a series of LED on/off states that form an image (see steps below) - -## Example: smiley - -```blocks -basic.showLeds(` -. . . . . -. # . # . -. . # . . -# . . . # -. # # # . -`) -``` - -## See also - -[show animation](/reference/basic/show-animation), [image](/reference/images/image), [show image](/reference/images/show-image), [scroll image](/reference/images/scroll-image) - diff --git a/docs/reference/basic/show-animation.md b/docs/reference/basic/show-animation.md deleted file mode 100644 index b91554e7a9e..00000000000 --- a/docs/reference/basic/show-animation.md +++ /dev/null @@ -1,64 +0,0 @@ -# show Animation - -Show a group of image frames (pictures) one after another on the [LED screen](/device/screen). It pauses the amount of time you tell it after each frame. - -```sig -basic.showAnimation(` -. . # . . . # # # . . # # # . -. # # . . . . . # . . . . # . -. . # . . . . # . . . # # # . -. . # . . . # . . . . . . # . -. . # . . . # # # . . # # # . -`) -``` - -## Parameters - -* `leds` is a [string](/types/string) that shows which LEDs are on and off, in groups one after another. -* `interval` is an optional [number](/types/number). It means the number of milliseconds to pause after each image frame. - -## Example: Animating a group of image frames - -In this animation, each row is 15 spaces wide because -there are three frames in the animation, and each frame is -five spaces wide, just like the screen on the @boardname@. - -```blocks -basic.showAnimation(` -. . # . . . # # # . . # # # . -. # # . . . . . # . . . . # . -. . # . . . . # . . . # # # . -. . # . . . # . . . . . . # . -. . # . . . # # # . . # # # . -`) -``` - -## ~hint - -If the animation is too fast, make `interval` bigger. - -## ~ - -## Example: animating frames with a pause - -This example shows six frames on the screen, pausing 500 milliseconds after each frame. - -In this animation, each row is 30 spaces wide because -there are six frames in the animation, and each frame is -five spaces wide, just like the screen. - -```blocks -basic.showAnimation(` -. . . . . # . . . . . . . . . . . . . # . . . . . # . . . . -. . # . . . . . . . . . # . . . . . . . . . # . . . . . . . -. # . # . . . # . . . # . # . . . # . . . # . # . . . # . . -. . # . . . . . . . . . # . . . . . . . . . # . . . . . . . -. . . . . . . . . # . . . . . # . . . . . . . . . . . . . # -`, 500) -``` - -## ~hint - -Use [forever](/reference/basic/forever) to show an animation over and over. - -## ~ diff --git a/docs/reference/basic/show-arrow.md b/docs/reference/basic/show-arrow.md index 82f60f4d7b7..3816f1087a2 100644 --- a/docs/reference/basic/show-arrow.md +++ b/docs/reference/basic/show-arrow.md @@ -25,5 +25,5 @@ for (let index = 0; index <= 7; index++) { ## See also -[showIcon](/reference/basic/show-icon), -[showLeds](/reference/basic/show-leds) \ No newline at end of file +[show icon](/reference/basic/show-icon), +[show leds](/reference/basic/show-leds) \ No newline at end of file diff --git a/docs/reference/basic/show-leds.md b/docs/reference/basic/show-leds.md index 7125078bd23..9765e3e4293 100644 --- a/docs/reference/basic/show-leds.md +++ b/docs/reference/basic/show-leds.md @@ -19,11 +19,15 @@ basic.showLeds(` * `interval` is an optional [number](/types/number) that means how many milliseconds to wait after showing a picture. If you are programming with blocks, `interval` is set at 400 milliseconds. -## ~ hint +### ~ hint -See how the @boardname@ shows numbers, text, and displays images by watching this video about [LEDs](https://www.youtube.com/watch?v=qqBmvHD5bCw). +#### LED display -## ~ +Watch this video to see how the @boardname@ shows numbers, text, and displays images with [LEDs](/reference/led). + +https://www.youtube.com/watch?v=qqBmvHD5bCw + +### ~ ## Example @@ -49,5 +53,4 @@ on and `.` means an LED that is turned off. ## See also -[plot leds](/reference/basic/plot-leds), [show animation](/reference/basic/show-animation) - +[show icon](/reference/basic/show-icon) diff --git a/docs/reference/basic/show-number.md b/docs/reference/basic/show-number.md index 804b96adc32..5bfea4cf53e 100644 --- a/docs/reference/basic/show-number.md +++ b/docs/reference/basic/show-number.md @@ -8,7 +8,7 @@ basic.showNumber(2) ## Parameters -* `value` is a [Number](/types/number). +* `value` is a [Number](/types/number). If the number is not single-digit number, it will scroll on the display. * `interval` is an optional [Number](/types/number). It means the number of milliseconds before sliding the `value` left by one LED each time. Bigger intervals make the sliding slower. ## Examples: @@ -37,12 +37,15 @@ for (let i = 0; i < 6; i++) { } ``` +## Advanced + +If `value` is `NaN` (not a number), `?` is displayed. + ## Other show functions -* Use [show string](/reference/basic/show-string) to show a [String](/types/string) with letters on the screen. -* Use [show animation](/reference/basic/show-animation) to show a group of pictures on the screen, one after another. +Use [show string](/reference/basic/show-string) to show a [String](/types/string) with letters on the screen. ## See also -[show string](/reference/basic/show-string), [show animation](/reference/basic/show-animation), [Number](/types/number), [math](/blocks/math) +[show string](/reference/basic/show-string), [Number](/types/number), [math](/blocks/math) diff --git a/docs/reference/basic/show-string.md b/docs/reference/basic/show-string.md index 80d831a9c2f..c437c4f8f57 100644 --- a/docs/reference/basic/show-string.md +++ b/docs/reference/basic/show-string.md @@ -28,10 +28,8 @@ basic.showString(s) ## Other show functions -* Use [show number](/reference/basic/show-number) to show a number on the [LED screen](/device/screen). -* Use [show animation](/reference/basic/show-animation) to show a group of pictures on the screen, one after another. +Use [show number](/reference/basic/show-number) to show a number on the [LED screen](/device/screen). ## See also -[String](/types/string), [show number](/reference/basic/show-number), [show animation](/reference/basic/show-animation) - +[String](/types/string), [show number](/reference/basic/show-number) diff --git a/docs/reference/bluetooth.md b/docs/reference/bluetooth.md index 1a20ac00c00..a691b9b8fd8 100644 --- a/docs/reference/bluetooth.md +++ b/docs/reference/bluetooth.md @@ -3,6 +3,7 @@ Support for additional Bluetooth services. ## ~hint + ![](/static/bluetooth/Bluetooth_SIG.png) For another device like a smartphone to use any of the Bluetooth "services" which the @boardname@ has, it must first be [paired with the @boardname@](/reference/bluetooth/bluetooth-pairing). Once paired, the other device may connect to the @boardname@ and exchange data relating to many of the @boardname@'s features. @@ -34,14 +35,6 @@ bluetooth.uartWriteValue("", 0); bluetooth.onUartDataReceived(",", () => {}) ``` -## Eddystone - -```cards -bluetooth.advertiseUid(42, 1, 7, true); -bluetooth.advertiseUrl("https://makecode.microbit.org/", 7, true); -bluetooth.stopAdvertising(); -``` - ## Advanced For more advanced information on the @boardname@ Bluetooth UART service including information on using a smartphone, see the [Lancaster University @boardname@ runtime technical documentation](http://lancaster-university.github.io/microbit-docs/ble/uart-service/) @@ -56,9 +49,7 @@ For more advanced information on the @boardname@ Bluetooth UART service includin [uartWriteNumber](/reference/bluetooth/uart-write-number), [uartWriteValue](/reference/bluetooth/uart-write-value), [onBluetoothConnected](/reference/bluetooth/on-bluetooth-connected), -[onBluetoothDisconnected](/reference/bluetooth/on-bluetooth-disconnected), -[advertiseUrl](/reference/bluetooth/advertise-url), -[stopAdvertising](/reference/bluetooth/stop-advertising) +[onBluetoothDisconnected](/reference/bluetooth/on-bluetooth-disconnected) ```package bluetooth diff --git a/docs/reference/bluetooth/advertise-uid-buffer.md b/docs/reference/bluetooth/advertise-uid-buffer.md index f4219e8556a..c703c74cebe 100644 --- a/docs/reference/bluetooth/advertise-uid-buffer.md +++ b/docs/reference/bluetooth/advertise-uid-buffer.md @@ -2,6 +2,18 @@ Advertises a UID via the Eddystone protocol over Bluetooth. +```sig +bluetooth.advertiseUidBuffer(pins.createBuffer(16), 7, true); +``` + +### ~ reminder + +#### Deprecated + +This API is deprecated. The Eddystone beacon format is no longer supported, see [Google Beacon format (Deprecated)](https://developers.google.com/beacons/eddystone). + +### ~ + ## ~hint ## Eddystone @@ -17,10 +29,6 @@ Read more at https://lancaster-university.github.io/microbit-docs/ble/eddystone/ ## ~ -```sig -bluetooth.advertiseUidBuffer(pins.createBuffer(16), 7, true); -``` - ## Parameters * ``buffer`` - a 16 bytes buffer containing the namespace (first 10 bytes) and instance (last 6 bytes). diff --git a/docs/reference/bluetooth/advertise-uid.md b/docs/reference/bluetooth/advertise-uid.md index 710b677d93a..e2192f5c536 100644 --- a/docs/reference/bluetooth/advertise-uid.md +++ b/docs/reference/bluetooth/advertise-uid.md @@ -2,6 +2,18 @@ Advertises a UID via the Eddystone protocol over Bluetooth. +```sig +bluetooth.advertiseUid(42, 1, 7, true); +``` + +### ~ reminder + +#### Deprecated + +This API is deprecated. The Eddystone beacon format is no longer supported, see [Google Beacon format (Deprecated)](https://developers.google.com/beacons/eddystone). + +### ~ + ## ~hint ## Eddystone @@ -17,10 +29,6 @@ Read more at https://lancaster-university.github.io/microbit-docs/ble/eddystone/ ## ~ -```sig -bluetooth.advertiseUid(42, 1, 7, true); -``` - ## Parameters * ``namespace`` last 4 bytes of the namespace uid (6 to 9) diff --git a/docs/reference/bluetooth/advertise-url.md b/docs/reference/bluetooth/advertise-url.md index 45a687f10cd..f876c9c02e0 100644 --- a/docs/reference/bluetooth/advertise-url.md +++ b/docs/reference/bluetooth/advertise-url.md @@ -2,6 +2,18 @@ Advertises a URL via the Eddystone protocol over Bluetooth. +```sig +bluetooth.advertiseUrl("https://makecode.microbit.org/", 7, true); +``` + +### ~ reminder + +#### Deprecated + +This API is deprecated. The Eddystone beacon format is no longer supported, see [Google Beacon format (Deprecated)](https://developers.google.com/beacons/eddystone). + +### ~ + ## ~hint ## Eddystone @@ -17,10 +29,6 @@ Read more at https://lancaster-university.github.io/microbit-docs/ble/eddystone/ ## ~ -```sig -bluetooth.advertiseUrl("https://makecode.microbit.org/", 7, true); -``` - ## Parameters * ``url`` - a [string](/types/string) containing the URL to broadcast, at most 17 characters long, excluding the protocol (eg: ``https://``) which gets encoded as 1 byte. diff --git a/docs/reference/control.md b/docs/reference/control.md index eb644622abe..5aac3602acf 100644 --- a/docs/reference/control.md +++ b/docs/reference/control.md @@ -4,7 +4,7 @@ Runtime and event utilities. ```cards control.inBackground(() => { - + }); control.reset(); control.waitMicros(4); diff --git a/docs/reference/control/hardware-version.md b/docs/reference/control/hardware-version.md new file mode 100644 index 00000000000..dfc65299186 --- /dev/null +++ b/docs/reference/control/hardware-version.md @@ -0,0 +1,15 @@ +# hardware Version + +Returns a string containing the the major version and minor version of the @boardname@. + +```sig +control.hardwareVersion() +``` + +## Returns + +* a [string](/types/string) that contains the major and minor versions of the @boardname@. The string has an `"x.y"` format like `"1.5"`, `"2.1"`, or `"2.2"`. + +## See also + +[device name](/reference/control/device-name), [device serial number](/reference/control/device-serial-number) \ No newline at end of file diff --git a/docs/reference/devices.md b/docs/reference/devices.md deleted file mode 100644 index 487d4cb1334..00000000000 --- a/docs/reference/devices.md +++ /dev/null @@ -1,33 +0,0 @@ -# Devices - -Control a phone with the @boardname@ via Bluetooth. - -## ~ hint - -**App required** You must use one of the [micro:bit apps](https://microbit.org/guide/mobile/) to use this functionality. - -## ~ - -```cards -devices.tellCameraTo(MesCameraEvent.TakePhoto); -devices.tellRemoteControlTo(MesRemoteControlEvent.play); -devices.raiseAlertTo(MesAlertEvent.DisplayToast); -devices.onNotified(MesDeviceInfo.IncomingCall, () => { - -}); -devices.onGamepadButton(MesDpadButtonInfo.ADown, () => { - -}); -devices.signalStrength(); -devices.onSignalStrengthChanged(() => { - -}); -``` - -```package -devices -``` - -## See Also - -[tellCameraTo](/reference/devices/tell-camera-to), [tellRemoteControlTo](/reference/devices/tell-remote-control-to), [raiseAlertTo](/reference/devices/raise-alert-to), [onNotified](/reference/devices/on-notified), [onGamepadButton](/reference/devices/on-gamepad-button), [signalStrength](/reference/devices/signal-strength), [onSignalStrengthChanged](/reference/devices/on-signal-strength-changed) diff --git a/docs/reference/devices/on-gamepad-button.md b/docs/reference/devices/on-gamepad-button.md deleted file mode 100644 index 14e74931a1c..00000000000 --- a/docs/reference/devices/on-gamepad-button.md +++ /dev/null @@ -1,25 +0,0 @@ -# On Gamepad Button - -Register code to run when the @boardname@ receives a command from the paired gamepad. - -## ~hint - -**App required** You must use one of the [micro:bit apps](https://microbit.org/guide/mobile/) to use this functionality. - -## ~ - -```sig -devices.onGamepadButton(MesDpadButtonInfo.ADown, () => {}) -``` - -## Parameters - -* ``body``: Action code to run when the the @boardname@ receives a command from the paired gamepad. - -## See Also - -[tell remote control to](/reference/devices/tell-remote-control-to), [raise alert to](/reference/devices/raise-alert-to), [signal strength](/reference/devices/signal-strength), [on signal strength changed](/reference/devices/on-signal-strength-changed) - -```package -devices -``` \ No newline at end of file diff --git a/docs/reference/devices/on-notified.md b/docs/reference/devices/on-notified.md deleted file mode 100644 index 8f86bb19d6e..00000000000 --- a/docs/reference/devices/on-notified.md +++ /dev/null @@ -1,35 +0,0 @@ -# On Notified - -Register code to run when the signal strength of the paired device changes. - -## ~hint - -**App required** You must use one of the [micro:bit apps](https://microbit.org/guide/mobile/) to use this functionality. - -## ~ - -```sig -devices.onNotified(MesDeviceInfo.IncomingCall, () => {}) -``` - -## Parameters - -* ``body``: code to run when the signal strength changes. - -## Examples - -Display the signal strength on screen: - -```blocks -devices.onNotified(MesDeviceInfo.IncomingCall, () => { - basic.showString("RING RING") -}) -``` - -## See Also - -[tell remote control to](/reference/devices/tell-remote-control-to), [raise alert to](/reference/devices/raise-alert-to), [signal strength](/reference/devices/signal-strength) - -```package -devices -``` \ No newline at end of file diff --git a/docs/reference/devices/on-signal-strength-changed.md b/docs/reference/devices/on-signal-strength-changed.md deleted file mode 100644 index 14db9594ed3..00000000000 --- a/docs/reference/devices/on-signal-strength-changed.md +++ /dev/null @@ -1,37 +0,0 @@ -# On Signal Strength Changed - -Register code to run when the signal strength of the paired device changes. - -## ~hint - -**App required** You must use one of the [micro:bit apps](https://microbit.org/guide/mobile/) to use this functionality. - -## ~ - - - -```sig -devices.onSignalStrengthChanged(() => {}) -``` - -## Parameters - -* ``body``: code to run when the signal strength changes. - -## Examples - -Display the signal strength on screen: - -```blocks -devices.onSignalStrengthChanged(() => { - basic.showNumber(devices.signalStrength()) -}) -``` - -## See Also - -[tell remote control to](/reference/devices/tell-remote-control-to), [raise alert to](/reference/devices/raise-alert-to), [signal strength](/reference/devices/signal-strength) - -```package -devices -``` \ No newline at end of file diff --git a/docs/reference/devices/raise-alert-to.md b/docs/reference/devices/raise-alert-to.md deleted file mode 100644 index 9371ffe7639..00000000000 --- a/docs/reference/devices/raise-alert-to.md +++ /dev/null @@ -1,65 +0,0 @@ -# raise alert to - -Raise an alert on a remote device. - -## ~hint - -**App required** You must use one of the [micro:bit apps](https://microbit.org/guide/mobile/) to use this functionality. - -## ~ - - - -```sig -devices.raiseAlertTo(MesAlertEvent.Vibrate) -``` - -## Parameters - -* event - an event identifier - -## Examples - -To tell the connected device to display toast - -```blocks -devices.raiseAlertTo(MesAlertEvent.DisplayToast) -``` - -To tell the connected device to vibrate - -```blocks -devices.raiseAlertTo(MesAlertEvent.Vibrate) -``` - -To tell the connected device to play a sound - -```blocks -devices.raiseAlertTo(MesAlertEvent.PlaySound) -``` - -To tell the connected device to play a ringtone - -```blocks -devices.raiseAlertTo(MesAlertEvent.PlayRingtone) -``` - -To tell the connected device to find my phone - -```blocks -devices.raiseAlertTo(MesAlertEvent.FindMyPhone) -``` - -To tell the connected device to ring alarm - -```blocks -devices.raiseAlertTo(MesAlertEvent.RingAlarm) -``` - -## See also - -[tell remote control to](/reference/devices/tell-remote-control-to), [tell camera to](/reference/devices/tell-camera-to) - -```package -devices -``` \ No newline at end of file diff --git a/docs/reference/devices/signal-strength.md b/docs/reference/devices/signal-strength.md deleted file mode 100644 index 5eb2b6a64c2..00000000000 --- a/docs/reference/devices/signal-strength.md +++ /dev/null @@ -1,36 +0,0 @@ -# Signal Strength - -Returns the signal strength reported by the paired device from ``0`` (no signal) to ``4`` (full strength). - -## ~hint - -**App required** You must use one of the [micro:bit apps](https://microbit.org/guide/mobile/) to use this functionality. - -## ~ - - -```sig -devices.signalStrength(); -``` - -## Returns - -* the signal strength from ``0`` (no signal) to ``4`` (full strength). - -## Examples - -Display the signal strength on screen: - -```blocks -devices.onSignalStrengthChanged(() => { - basic.showNumber(devices.signalStrength()) -}) -``` - -## See Also - -[tell remote control to](/reference/devices/tell-remote-control-to), [raise alert to](/reference/devices/raise-alert-to), [on signal strength changed](/reference/devices/on-signal-strength-changed) - -```package -devices -``` \ No newline at end of file diff --git a/docs/reference/devices/tell-camera-to.md b/docs/reference/devices/tell-camera-to.md deleted file mode 100644 index 3870d785d63..00000000000 --- a/docs/reference/devices/tell-camera-to.md +++ /dev/null @@ -1,76 +0,0 @@ -# tell camera to - -Access the photo/video-taking functionality of a remote device using the ``tell camera to`` function. - -## ~hint - -**App required** You must use one of the [micro:bit apps](https://microbit.org/guide/mobile/) to use this functionality. - -## ~ - - -```sig -devices.tellCameraTo(MesCameraEvent.TakePhoto) -``` - -## Parameters - -* event - an event identifier - -## Examples - -To tell the connected device to take a picture: - -```blocks -devices.tellCameraTo(MesCameraEvent.TakePhoto) -``` - -To tell the connected device to start recording a video: - -```blocks -devices.tellCameraTo(MesCameraEvent.StartVideoCapture) -``` - -To tell the connected device to stop recording a video: - -```blocks -devices.tellCameraTo(MesCameraEvent.StopVideoCapture) -``` - -To tell the connected device to toggle front-rear: - -```blocks -devices.tellCameraTo(MesCameraEvent.ToggleFrontRear) -``` - -To tell the connected device to launch photo mode: - -```blocks -devices.tellCameraTo(MesCameraEvent.LaunchPhotoMode) -``` - -To tell the connected device to launch video mode: - -```blocks -devices.tellCameraTo(MesCameraEvent.LaunchVideoMode) -``` - -To tell the connected device to stop photo mode: - -```blocks -devices.tellCameraTo(MesCameraEvent.StopPhotoMode) -``` - -To tell the connected device to stop video mode: - -```blocks -devices.tellCameraTo(MesCameraEvent.StopVideoMode) -``` - -## See Also - -[tell remote control to](/reference/devices/tell-remote-control-to), [raise alert to](/reference/devices/raise-alert-to) - -```package -devices -``` diff --git a/docs/reference/devices/tell-remote-control-to.md b/docs/reference/devices/tell-remote-control-to.md deleted file mode 100644 index 51639734617..00000000000 --- a/docs/reference/devices/tell-remote-control-to.md +++ /dev/null @@ -1,89 +0,0 @@ -# tell remote control to - -Control the presentation of media content available on a remote device using the `tell remote control` to function. - -## ~hint - -**App required** You must use one of the [micro:bit apps](https://microbit.org/guide/mobile/) to use this functionality. - -## ~ - - -```sig -devices.tellRemoteControlTo(MesRemoteControlEvent.play) -``` - -## Parameters - -* event - an event identifier - -## Event values - -* play -* stop -* pause -* forward -* rewind -* volume up -* volume down -* previous track -* next track - -## Examples - -To tell the connected device to start playing: - -```blocks -devices.tellRemoteControlTo(MesRemoteControlEvent.play) -``` - -To tell the connected device to stop playing - -```blocks -devices.tellRemoteControlTo(MesRemoteControlEvent.stop) -``` - -To tell the connected device to go to next track - -```blocks -devices.tellRemoteControlTo(MesRemoteControlEvent.nextTrack) -``` - -To tell the connected device to go to previous track - -```blocks -devices.tellRemoteControlTo(MesRemoteControlEvent.previousTrack) -``` - -To tell the connected device to go forward - -```blocks -devices.tellRemoteControlTo(MesRemoteControlEvent.forward) -``` - -To tell the connected device to rewind - -```blocks -devices.tellRemoteControlTo(MesRemoteControlEvent.rewind) -``` - -To tell the connected device volume up - -```blocks -devices.tellRemoteControlTo(MesRemoteControlEvent.volumeUp) -``` - -To tell the connected device volume down - -```blocks -devices.tellRemoteControlTo(MesRemoteControlEvent.volumeDown) -``` - -## See also - -[tell camera to](/reference/devices/tell-camera-to), [raise alert to](/reference/devices/raise-alert-to) - - -```package -devices -``` diff --git a/docs/reference/game/change.md b/docs/reference/game/change.md index 00d9a231bdb..704e178c86e 100644 --- a/docs/reference/game/change.md +++ b/docs/reference/game/change.md @@ -1,19 +1,23 @@ # change (Sprite Property) -Change the kind of [number](/types/number) you say for a [sprite](/reference/game/create-sprite). +Change a value for a [sprite](/reference/game/create-sprite) property by some amount. ```sig game.createSprite(0,0).change(LedSpriteProperty.X, 0); ``` +The value of a sprite propery is changed by using either a positive or negative number. Giving `1` will increase a property value by `1` and giving a `-1` will decrease it by `1`. + ## Parameters * **property**: the property of the **Sprite** you want to change, like: ->* ``x`` - how far up or down the sprite is on the screen (`0`-`4`) ->* ``y`` - how far left or right the sprite is on the screen (`0`-`4`) ->* ``direction`` - which way the sprite is pointing (this works the same way as the [turn](/reference/game/turn) function) ->* ``brightness`` - how bright the LED sprite is (this works the same way as the [brightness](/reference/led/brightness) function) ->* ``blink`` - how fast the sprite is blinking (the bigger the number is, the faster the sprite is blinking) +>* ``x`` - the change in horizontal location to set the sprite at on the LED screen (`0`-`4`) +>* ``y`` - the change vertical location to set the sprite at on the LED screen (`0`-`4`) +>* ``direction`` - the change of direction in degrees for the sprite to go when the next [move](/reference/game/move) happens. Direction degree range is from `-180` to `180`. +>* ``brightness`` - the change in brightness for the LED sprite. Completely dark is `0` and very bright is `255`. +>* ``blink`` - the change in how fast the sprite is will blink on and off. The blink rate is in milliseconds. + +* **value**: a [number](/types/number) value that is the amount of change for the property. ## Example diff --git a/docs/reference/game/clear.md b/docs/reference/game/clear.md index 93b89c15c4d..036d9f21588 100644 --- a/docs/reference/game/clear.md +++ b/docs/reference/game/clear.md @@ -35,5 +35,5 @@ input.onButtonPressed(Button.A, () => { ## See also -[Image](/reference/images/image), [show animation](/reference/basic/show-animation), [show image](/reference/images/show-image), [scroll image](/reference/images/scroll-image), [create image](/reference/images/create-image) +[Image](/reference/images/image), [show image](/reference/images/show-image), [scroll image](/reference/images/scroll-image), [create image](/reference/images/create-image) diff --git a/docs/reference/game/get.md b/docs/reference/game/get.md index d2608e3ea65..7e615871349 100644 --- a/docs/reference/game/get.md +++ b/docs/reference/game/get.md @@ -1,6 +1,6 @@ # get (Sprite Property) -Find something out about a [sprite](/reference/game/create-sprite). +Get a value for a [sprite](/reference/game/create-sprite) property. ```sig game.createSprite(0,0).get(LedSpriteProperty.X); @@ -9,11 +9,11 @@ game.createSprite(0,0).get(LedSpriteProperty.X); ## Parameters * **property**: the property of the **Sprite** you want to know about, like: ->* ``x`` - how far up or down the sprite is on the screen (`0`-`4`) ->* ``y`` - how far left or right the sprite is on the screen (`0`-`4`) ->* ``direction`` - which way the sprite is pointing (this works the same way as the [turn](/reference/game/turn) function) ->* ``brightness`` - how bright the LED sprite is (this works the same way as the [brightness](/reference/led/brightness) function) ->* ``blink`` - how fast the sprite is blinking (the bigger the number is, the faster the sprite is blinking) +>* ``x`` - the horizontal location to set the sprite at on the LED screen (`0`-`4`) +>* ``y`` - the vertical location to set the sprite at on the LED screen (`0`-`4`) +>* ``direction`` - the direction in degrees for the sprite to go when the next [move](/reference/game/move) happens. The degree range is from `-180` to `180`. +>* ``brightness`` - how bright the LED sprite is. Completely dark is `0` and very bright is `255`. +>* ``blink`` - how fast the sprite is will blink on and off. The blink rate is in milliseconds. ## Returns diff --git a/docs/reference/game/set.md b/docs/reference/game/set.md index 7a6ccb1b503..27fa922416e 100644 --- a/docs/reference/game/set.md +++ b/docs/reference/game/set.md @@ -1,6 +1,6 @@ # set (Sprite Property) -Make a [sprite](/reference/game/create-sprite) store the kind of [number](/types/number) you say. +Set a value for a [sprite](/reference/game/create-sprite) property. ```sig game.createSprite(0,0).set(LedSpriteProperty.X, 0); @@ -9,22 +9,39 @@ game.createSprite(0,0).set(LedSpriteProperty.X, 0); ## Parameters * **property**: the property of the **Sprite** you want to store a value for, like: ->* ``x`` - how far up or down the sprite is on the screen (`0`-`4`) ->* ``y`` - how far left or right the sprite is on the screen (`0`-`4`) ->* ``direction`` - which way the sprite is pointing (this works the same way as the [turn](/reference/game/turn) function) ->* ``brightness`` - how bright the LED sprite is (this works the same way as the [brightness](/reference/led/brightness) function) ->* ``blink`` - how fast the sprite is blinking (the bigger the number is, the faster the sprite is blinking) +>* ``x`` - the horizontal location to set the sprite at on the LED screen (`0`-`4`) +>* ``y`` - the vertical location to set the sprite at on the LED screen (`0`-`4`) +>* ``direction`` - the direction in degrees for the sprite to go when the next [move](/reference/game/move) happens. The degree range is from `-180` to `180`. +>* ``brightness`` - how bright the LED sprite is. Completely dark is `0` and very bright is `255`. +>* ``blink`` - how fast the sprite is will blink on and off. The blink rate is in milliseconds. + +* **value**: the a [number](/types/number) value to set for the property. ## Example -This program makes a sprite on the left side of the screen, -waits two seconds (2000 milliseconds), -and then moves it to the right side of the screen. +Make an LED sprite move to random locations on the screen. Use button **A** to freeze and unfreeze the sprite while it's moving. When the sprite is frozen, it will blink and dim to half brightness. ```blocks -let ball = game.createSprite(0, 2); -basic.pause(2000); -ball.set(LedSpriteProperty.X, 4); +input.onButtonPressed(Button.A, function () { + if (freeze) { + sprite.set(LedSpriteProperty.Brightness, 255) + sprite.set(LedSpriteProperty.Blink, 0) + } else { + sprite.set(LedSpriteProperty.Brightness, 128) + sprite.set(LedSpriteProperty.Blink, 200) + } + freeze = !(freeze) +}) +let freeze = false +let sprite: game.LedSprite = null +sprite = game.createSprite(0, 0) +basic.forever(function () { + if (!(freeze)) { + sprite.set(LedSpriteProperty.X, randint(0, 4)) + sprite.set(LedSpriteProperty.Y, randint(0, 4)) + } + basic.pause(500) +}) ``` ## See also diff --git a/docs/reference/images/create-big-image.md b/docs/reference/images/create-big-image.md index c5c75db13b0..6784d1a870c 100644 --- a/docs/reference/images/create-big-image.md +++ b/docs/reference/images/create-big-image.md @@ -49,4 +49,4 @@ input.onButtonPressed(Button.B, () => { [image](/reference/images/image), [create image](/reference/images/create-image), [show image](/reference/images/show-image), -[scroll image](/reference/images/scroll-image), [show animation](/reference/basic/show-animation) +[scroll image](/reference/images/scroll-image) \ No newline at end of file diff --git a/docs/reference/images/create-image.md b/docs/reference/images/create-image.md index d4ab2563932..3d4d99c05a3 100644 --- a/docs/reference/images/create-image.md +++ b/docs/reference/images/create-image.md @@ -32,7 +32,7 @@ input.onButtonPressed(Button.A, () => { # . # . # . . # . . . . # . . - `).showImage(0); + `).showImage(0) }); input.onButtonPressed(Button.B, () => { images.createImage(` @@ -41,8 +41,8 @@ input.onButtonPressed(Button.B, () => { # . # . # . # # # . . . # . . - `).showImage(0); -}); + `).showImage(0) +}) ``` ## See also @@ -50,5 +50,4 @@ input.onButtonPressed(Button.B, () => { [image](/reference/images/image), [create big image](/reference/images/create-big-image), [show image](/reference/images/show-image), -[scroll image](/reference/images/scroll-image), [show animation](/reference/basic/show-animation) - +[scroll image](/reference/images/scroll-image) diff --git a/docs/reference/images/plot-frame.md b/docs/reference/images/plot-frame.md index 0db48dcb1e1..9ea14c0855d 100644 --- a/docs/reference/images/plot-frame.md +++ b/docs/reference/images/plot-frame.md @@ -33,5 +33,5 @@ img.plotFrame(1) ## See also -[create image](/reference/images/create-image), [show animation](/reference/basic/show-animation), [image](/reference/images/image), [show image](/reference/images/show-image), [scroll image](/reference/images/scroll-image) +[create image](/reference/images/create-image), [image](/reference/images/image), [show image](/reference/images/show-image), [scroll image](/reference/images/scroll-image) diff --git a/docs/reference/images/plot-image.md b/docs/reference/images/plot-image.md index df87bc6501b..ef13335f61e 100644 --- a/docs/reference/images/plot-image.md +++ b/docs/reference/images/plot-image.md @@ -33,5 +33,5 @@ img.plotImage(0) ## See also -[create image](/reference/images/create-image), [show animation](/reference/basic/show-animation), [image](/reference/images/image), [show image](/reference/images/show-image), [scroll image](/reference/images/scroll-image) +[create image](/reference/images/create-image), [image](/reference/images/image), [show image](/reference/images/show-image), [scroll image](/reference/images/scroll-image) diff --git a/docs/reference/images/scroll-image.md b/docs/reference/images/scroll-image.md index 7821ec2e60b..0480727b561 100644 --- a/docs/reference/images/scroll-image.md +++ b/docs/reference/images/scroll-image.md @@ -46,5 +46,4 @@ basic.forever(() => { ## See also -[show image](/reference/images/show-image), [image](/reference/images/image), [create image](/reference/images/create-image), [show animation](/reference/basic/show-animation) - +[show image](/reference/images/show-image), [image](/reference/images/image), [create image](/reference/images/create-image) diff --git a/docs/reference/images/show-frame.md b/docs/reference/images/show-frame.md index d0e72154fca..d394dae5b80 100644 --- a/docs/reference/images/show-frame.md +++ b/docs/reference/images/show-frame.md @@ -33,5 +33,5 @@ img.showFrame(1) ## See also -[create image](/reference/images/create-image), [show animation](/reference/basic/show-animation), [image](/reference/images/image), [show image](/reference/images/show-image), [scroll image](/reference/images/scroll-image) +[create image](/reference/images/create-image), [image](/reference/images/image), [show image](/reference/images/show-image), [scroll image](/reference/images/scroll-image) diff --git a/docs/reference/images/show-image.md b/docs/reference/images/show-image.md index 3867e500844..e0a97a0f693 100644 --- a/docs/reference/images/show-image.md +++ b/docs/reference/images/show-image.md @@ -44,4 +44,4 @@ input.onButtonPressed(Button.B, () => { [image](/reference/images/image), [create image](/reference/images/create-image), [create big image](/reference/images/create-big-image), -[scroll image](/reference/images/scroll-image), [show animation](/reference/basic/show-animation) +[scroll image](/reference/images/scroll-image) diff --git a/docs/reference/images/width.md b/docs/reference/images/width.md index e81f2e1eafc..38d88a3cc01 100644 --- a/docs/reference/images/width.md +++ b/docs/reference/images/width.md @@ -52,5 +52,4 @@ for (let i = 0; i < img2.width() / 5; i++) { ## See also -[show image](/reference/images/show-image), [image](/reference/images/image), [create image](/reference/images/create-image), [scroll image](/reference/images/scroll-image), [show animation](/reference/basic/show-animation) - +[show image](/reference/images/show-image), [image](/reference/images/image), [create image](/reference/images/create-image), [scroll image](/reference/images/scroll-image) diff --git a/docs/reference/input/compass-heading.md b/docs/reference/input/compass-heading.md index 5696c29ea40..0066d1b7ca9 100644 --- a/docs/reference/input/compass-heading.md +++ b/docs/reference/input/compass-heading.md @@ -1,40 +1,99 @@ -# Compass Heading +# compass Heading Find which direction on a compass the @boardname@ is facing. The @boardname@ measures the **compass heading** from `0` to `359` -degrees with its **magnetometer** chip. Different numbers mean north, -east, south, and west. +degrees with its **magnetometer** chip. Different numbers mean North, +East, South, and West. ```sig -input.compassHeading(); +input.compassHeading() ``` ## Returns * a [number](/types/number) from `0` to `359` degrees, which means the compass heading. If the compass isn't ready, it returns `-1003`. -## Example +## Compass points -This program finds the compass heading and stores it in the -`degrees` variable. +In history, a compass was a device that pointed in the direction of the magnetic North Pole. A needle or or a spot on a moving dial was magnetically attracted to the pole. Using a circular card or diagram arranged with direction indications you could align the indicator with the mark that meant "North" and see which direction the heading you wanted was in. -```blocks -let degrees = input.compassHeading() +![Modern plastic compass](/static/device/compass/plastic-compass.jpg)
+**Magnetic compass**
+_by Evan Amos, Public Domain_ + +The compass pointer (needle) is always pointing to magnetic North. The compass dial might not have alignment with the needle. Once the needle is aligned with the dial, the compass will show what the direction is for a heading. The basic 4 compass points are **North (W), East (E), South (S),** and **West (W)**. These are also called the _Four Cardinal Directions_. + +In the compass examples below, the needle is pointing toward magnetic North. Next, the needle becomes aligned with the "N" symbol. If you wanted to walk East, you would take the compass and turn it so the "E" symbol is pointing directly ahead of you. While you walk East, keep the needle aligned with the "N" symbol. + +| Needle Heading | Compass Aligned | +|-|-| +| ![Unaligned compass](/static/device/compass/compass-needle.png) | ![Aligned compass](/static/device/compass/compass-align.png) + +## Directions and degrees + +Modern compasses also use degree markings along with direction symbols. The compass dial shows degree markings from `0°` to `360°`. + +![Compass with degree markings](/static/device/compass/compass-degrees.png) + +The degree markings map to directions. Below is a list of directions and their degree values. + +|Direction|Symbol|Degrees| +|-|-|-| +|North|N|0°| +|Northeast|NE|45°| +|East|E|90°| +|Southeast|SE|135°| +|South|S|180°| +|Southwest|SE|225°| +|West|W|270°| +|Northwest|NW|315°| + +
+ +Besides the 4 Cardinal Directions, there are *Intercardinal Directions* like Northeast (shown in the list above). Additionally, there are direction names between the Cardinal and Intercardinal ones. One of these is North-Northeast (NNE) whose degree value is 22.5° and another is West-Southwest (WSW) at 247.5°. + +### ~tip + +#### Running compass programs on the @boardname@ + +When testing and using your compass programs on the board, hold the @boardname@ face up (logo side up) and the top edge toward the direction to measure (bottom edge connector side is toward your body). + +### ~ + +### ~hint + +#### Compass simulation + +When you run a program in the simulator that uses ``||input:compassHeading||``, a compass direction needle appears on the screen. Click and rotate the direction needle to change the compass heading. + +```sim +basic.forever(function () { + basic.showNumber(input.compassHeading()) + basic.pause(5000) +}) ``` -## ~hint +### ~ -When you run a program that uses this function in a browser, click and drag -the compass needle on the screen to change the compass heading. +## Examples -## ~ +### Digital compass -## Example: compass +Display the current compass heading in degrees `0` - `359`. -This program finds the compass heading and then shows a letter -that means whether the @boardname@ is facing north (N), south (S), -east (E), or west (W). +```blocks +basic.forever(function () { + basic.showNumber(input.compassHeading()) + basic.pause(1000) +}) +``` + +### Analog compass + +Find the compass heading and then show an arrow +that means whether the @boardname@ is facing north (🠉), south (🠋), +east (🠊), or west (🠈). ```blocks let degrees = 0 @@ -65,9 +124,11 @@ will ask you to draw a fill pattern on the screen by tilting the @boardname@. If you are calibrating or using the compass near metal, it might confuse the @boardname@. -## ~ hint +### ~ hint + +#### Make a calibration tool -Keep the calibration handy by running it when the user pressed **A+B**. +Keep the calibration current by running it when the user pressed **A+B**. ```block input.onButtonPressed(Button.AB, () => { @@ -75,7 +136,7 @@ input.onButtonPressed(Button.AB, () => { }) ``` -## ~ +### ~ ## See also diff --git a/docs/reference/input/logo-is-pressed.md b/docs/reference/input/logo-is-pressed.md index 7e29a0e83ef..22d3281066d 100644 --- a/docs/reference/input/logo-is-pressed.md +++ b/docs/reference/input/logo-is-pressed.md @@ -38,5 +38,5 @@ basic.forever(function () { [micro:bit V2](/device/v2), [on logo event](/reference/input/on-logo-event), -[pin is pressed](/referene/inpu/pin-is-pressed), -[touch set mode](/referene/inpu/touch-set-mode) \ No newline at end of file +[pin is pressed](/reference/input/pin-is-pressed), +[touch set mode](/reference/pins/touch-set-mode) diff --git a/docs/reference/input/magnetic-force.md b/docs/reference/input/magnetic-force.md index 7c1b72f7515..5f61ec325fa 100644 --- a/docs/reference/input/magnetic-force.md +++ b/docs/reference/input/magnetic-force.md @@ -1,4 +1,4 @@ -# Magnetic Force +# magnetic Force Find the amount of magnetic force (the strength of a magnet) in one of the three directions. @@ -6,14 +6,21 @@ Find the amount of magnetic force (the strength of a magnet) in one of the three input.magneticForce(Dimension.X); ``` -## ~hint +The @boardname@ measures magnetic force in **microteslas**. -The @boardname@ measures magnetic force with **microteslas**. +### ~hint -You are asked to [calibrate](https://support.microbit.org/support/solutions/articles/19000008874-calibrating-the-micro-bit-compass) the compass the first time run a program -that uses the compass. +#### Compass calibration -## ~ +The magnetometer doesn't automatically calibrate to the Earth's magnetic field when reading magnetic force. This is so you can detect localized magnetic attractions in your tests and experiments. If you want to calibrate for magnetic polar alignment before measuring magnetic force, you need to first calibrate using: + +```block +input.calibrateCompass() +``` + +When you run this block you will be asked to [calibrate](https://support.microbit.org/support/solutions/articles/19000008874-calibrating-the-micro-bit-compass) for the compass. + +### ~ ## Parameters @@ -24,7 +31,7 @@ that uses the compass. ## Returns -* a [number](/types/number) of microteslas that means the strength of the magnet +* a [number](/types/number) of microteslas that is the strength of the magnetic force. ## Example @@ -39,4 +46,5 @@ basic.forever(function() { ## See also -[compass heading](/reference/input/compass-heading) +[compass heading](/reference/input/compass-heading), +[calibrate compass](/reference/input/calibrate-compass) diff --git a/docs/reference/input/on-logo-event.md b/docs/reference/input/on-logo-event.md index 180bdbafa29..b96b9f7a7a0 100644 --- a/docs/reference/input/on-logo-event.md +++ b/docs/reference/input/on-logo-event.md @@ -35,4 +35,4 @@ input.onLogoEvent(TouchButtonEvent.Pressed, function () { [micro:bit V2](/device/v2), [logo is pressed](/reference/input/logo-is-pressed), [on pin pressed](/reference/input/on-logo-released), -[touch set mode](/referene/inpu/touch-set-mode) +[touch set mode](/reference/pins/touch-set-mode) diff --git a/docs/reference/input/running-time-micros.md b/docs/reference/input/running-time-micros.md index 413cf4ef9eb..27e4955421e 100644 --- a/docs/reference/input/running-time-micros.md +++ b/docs/reference/input/running-time-micros.md @@ -1,17 +1,35 @@ -# Running Time Micros +# running Time Micros -Find how long it has been since the program started in micro-seconds. +Find how long a program has run since it was started, in microseconds. ```sig -input.runningTimeMicros(); +input.runningTimeMicros() +``` + +### ~ alert + +#### Running time maximum count + +The program running time counter can only count up to approximately 17 minutes of time (1,073,741,823 microseconds). After that, the running time will reset to zero and start counting up again. + +Programs using the time counter should take this reset into account, even if they are only measuring short durations. The counter might reset while the program is running. + +### ~ + +## Example + +Pause for one second and then show the program running time on the screen. + +```blocks +basic.pause(1000) +basic.showNumber(input.runningTimeMicros()) ``` ## Returns -* the [Number](/types/number) of microseconds since the program started. -(One second is 1000000 microseconds.) +* the [number](/types/number) of microseconds since the program started. +(One second is 1,000,000 microseconds.) ## See also -[show number](/reference/basic/show-number), [pause](/reference/basic/pause) - +[show number](/reference/basic/show-number), [pause](/reference/basic/pause), [running time](/reference/input/running-time) \ No newline at end of file diff --git a/docs/reference/input/running-time.md b/docs/reference/input/running-time.md index 099fb640902..e11dc7d5697 100644 --- a/docs/reference/input/running-time.md +++ b/docs/reference/input/running-time.md @@ -1,9 +1,9 @@ # Running Time -Find how long it has been since the program started in milli-seconds. +Find how long a program has run since it was started, in milliseconds. ```sig -input.runningTime(); +input.runningTime() ``` ## Returns @@ -13,12 +13,12 @@ input.runningTime(); ## Example: elapsed time -When you press button `B` on the microbit, this +When you press button `B` on the @boardname@, this program finds the number of milliseconds since the program started and shows it on the [LED screen](/device/screen). ```blocks -input.onButtonPressed(Button.B, () => { +input.onButtonPressed(Button.B, function() { let now = input.runningTime() basic.showNumber(now) }) @@ -27,5 +27,5 @@ input.onButtonPressed(Button.B, () => { ## See also -[show number](/reference/basic/show-number), [pause](/reference/basic/pause) +[show number](/reference/basic/show-number), [pause](/reference/basic/pause), [running time micros](/reference/input/running-time-micros) diff --git a/docs/reference/input/set-sound-threshold.md b/docs/reference/input/set-sound-threshold.md index 672a278bc56..7b5357c03f9 100644 --- a/docs/reference/input/set-sound-threshold.md +++ b/docs/reference/input/set-sound-threshold.md @@ -12,6 +12,14 @@ a sound level number as a _threshold_ (just the right amount of sound) to make t [on sound](/reference/input/on-sound) event happen. To set a threshold, you choose the type of sound to detect, `loud` or `quiet`, and then the sound level for that type. +### ~ reminder + +![works with micro:bit V2 only image](/static/v2/v2-only.png) + +This block requires the [micro:bit V2](/device/v2) hardware. If you use this block with a micro:bit v1 board, you will see the **927** error code on the screen. + +### ~ + ## Parameters * **sound**: the type of sound to dectect: `loud` or `quiet`. diff --git a/docs/reference/input/sound-level.md b/docs/reference/input/sound-level.md index 4ec428ca044..446d205b983 100644 --- a/docs/reference/input/sound-level.md +++ b/docs/reference/input/sound-level.md @@ -1,11 +1,19 @@ # sound Level -Find out what the the level of sound heard by microphone is. +Find out what the the level of sound heard by the microphone is. ```sig input.soundLevel() ``` +### ~ reminder + +![works with micro:bit V2 only image](/static/v2/v2-only.png) + +This block requires the [micro:bit V2](/device/v2) hardware. If you use this block with a micro:bit v1 board, you will see the **927** error code on the screen. + +### ~ + ## Returns * a ``number`` between `0` (low sound) and `255` (loud sound) which tells how loud the sounds are that the microphone hears. @@ -26,7 +34,7 @@ basic.forever(function () { ## See also -[on sound](/reference/input/on-sound), [set sound threshold](/reference/input/sound-level) +[on sound](/reference/input/on-sound), [set sound threshold](/reference/input/set-sound-threshold) ```package microphone diff --git a/docs/reference/led.md b/docs/reference/led.md index 105b865bf7a..1776027b217 100644 --- a/docs/reference/led.md +++ b/docs/reference/led.md @@ -19,4 +19,4 @@ led.enable(false) ## See Also [plot](/reference/led/plot), [unplot](/reference/led/unplot), [point](/reference/led/point), [brightness](/reference/led/brightness), [setBrightness](/reference/led/set-brightness), [stopAnimation](/reference/led/stop-animation), [plotBarGraph](/reference/led/plot-bar-graph), [toggle](/reference/led/toggle), [setDisplayMode](/reference/led/set-display-mode), [enabled](/reference/led/enable), -[plotBrightness](/reference/led/plot-brightness), +[plotBrightness](/reference/led/plot-brightness) diff --git a/docs/reference/led/plot-bar-graph.md b/docs/reference/led/plot-bar-graph.md index cb5bd6451aa..385ece04efb 100644 --- a/docs/reference/led/plot-bar-graph.md +++ b/docs/reference/led/plot-bar-graph.md @@ -6,7 +6,7 @@ Display a bar graph for a number value. led.plotBarGraph(2, 20); ``` -A bar graph is a kind of chart that shows numbers as lines with different lengths. +A bar graph is a kind of chart that shows numbers as lines with different lengths. The value is plotted in LEDs as a ratio of a **value** to the **high** value you set as a maximum range. So, if there are 25 LEDs on the screen, then plotting `9` for the high value of `50` would display about 5 LEDs on the screen. ## Parameters @@ -16,13 +16,13 @@ A bar graph is a kind of chart that shows numbers as lines with different length if the temperature is 0 degrees Celsius. * **high**: a [number](/types/number) that is the highest possible number (maximum) that the **value** parameter can be. The lines in the bar graph will reach their highest point when **value** reaches this number. If **high** is `0`, then the largest value recently plotted is used as the maximum. +* **valueToConsole**: a [boolean](/types/boolean) value that when `true` will also send the **value** to the serial port. A value of `false` will prevent the number in **value** from going to the serial output. ### ~hint #### Serial Output -The ``||led:plot bar graph||`` block also writes the number from **value** to the [serial](/reference/serial) port as a way to help you record -values. +The ``||led:plot bar graph||`` block will also write the number from **value** to the [serial](/reference/serial) port if you set the **valueToConsole** parameter to `true`. This is a way to help you record the values you've plotted. ### ~ @@ -32,12 +32,12 @@ Show a bar graph of the [acceleration](/reference/input/acceleration) in the `x` direction of the @boardname@. The @boardname@'s `x` direction is from left to right (or right to left). The faster you move the @boardname@ in this direction, -the taller the lines in the bar graph will be. The **high** paramter is `1023` which sets the highest possible value of acceleration to show. +the taller the lines in the bar graph will be. The **high** paramter is `1023` which sets the highest possible value of acceleration to show. Also, record the acceleration value by sending it to the serial port. ```blocks basic.forever(() => { let a = input.acceleration(Dimension.X); - led.plotBarGraph(a, 1023) + led.plotBarGraph(a, 1023, true) }) ``` diff --git a/docs/reference/led/stop-animation.md b/docs/reference/led/stop-animation.md index 050d0a2f9f3..c1376bc4661 100644 --- a/docs/reference/led/stop-animation.md +++ b/docs/reference/led/stop-animation.md @@ -29,4 +29,4 @@ to go. ## See Also -[show animation](/reference/basic/show-animation) +[show leds](/reference/basic/show-leds), [show icon](/reference/basic/show-icon), [plot](/reference/led/plot) diff --git a/docs/reference/loops/every-interval.md b/docs/reference/loops/every-interval.md new file mode 100644 index 00000000000..ab1c487b93b --- /dev/null +++ b/docs/reference/loops/every-interval.md @@ -0,0 +1,42 @@ +# every Interval + +Run part of the program in a loop continuously at a time interval. + +```sig +loops.everyInterval(500, function () {}) +``` + +If you want to run some code continuously, but on a time interval, then use an **every** loop. You set the amount of time that the loop waits before the code inside runs again. This is similar to a [forever](/reference/basic/forever) loop, in that it runs continuously, except that there's a time interval set to wait on before the loop runs the next time. This loop is useful when you want some of a program's code run on a _schedule_. + +## Parameters + +* **interval**: a [number](/types/number) that is the amount of time in milliseconds to wait before running the loop again. + +### ~ reminder + +#### Event-based loops + +Both the **every** loop and the **forever** loop are _event-based_ loops where the code inside is run as part of a function. These are different from the [for](/blocks/loops/for) and [while](/blocks/loops/while) loops. Those are loops are part of the programming language and can have [break](/blocks/loops/break) and [continue](/blocks/loops/continue) statements in them. +You can NOT use **break** or **continue** in either an **every** loop or a **forever** loop. + +### ~ + +## Example + +At every `200` milliseconds of time, check if either the **A** or **B** button is pressed. If so, show on the screen which one is pressed. + +```blocks +loops.everyInterval(200, function () { + if (input.buttonIsPressed(Button.A)) { + basic.showString("A") + } else if (input.buttonIsPressed(Button.B)) { + basic.showString("B") + } else { + basic.clearScreen() + } +}) +``` + +## See also + +[forever](/reference/basic/forever) \ No newline at end of file diff --git a/docs/reference/math/constant.md b/docs/reference/math/constant.md new file mode 100644 index 00000000000..93807dc5629 --- /dev/null +++ b/docs/reference/math/constant.md @@ -0,0 +1,131 @@ +# Constant + +A common mathematical constant value. + +```sig +Math._constant(Math.PI) +``` + +There are several constant values that are important in calculations for science and engineering. The ``||math.constant||`` block provides several that you can use in your mathematical formulas and expressions. + +## Ī€ + +The value of Pi (Ī€) which is the ratio of a circle's circumference to its diameter. + +```block +Math._constant(Math.PI) +``` + +### Example + +Get the area of a circle with a radius of `4`. + +```block +let circle_area = Math.PI * 4**2 +``` + +## e + +Euler's number (e) which is the base of the natural logarithm and the exponential function. + +```block +Math._constant(Math.E) +``` + +### Example + +Find the half-life, in years, of radioactive decay for Carbon-14. + +```block +let time = 0 +let decay = 0.000121 +let carbon14 = 1 +while (carbon14 > 0.5) { + carbon14 = Math.E ** (-1 * decay * time) + time += 1 +} +``` + +## ln(2) + +Natural log of 2. + +```block +Math._constant(Math.LN2) +``` + +### Example + +Find out how many years will it take to double an investment using continuous compounding at 5% interest. + +```block +let years = Math.LN2 / 0.05 +``` + +## ln(10) + +Natural log of 10. + +```block +Math._constant(Math.LN10) +``` + +### Example + +How many days will it take for bacteria in a culture to reach 10 times the start amount if they double in number each day. + +```block +let days = Math.LN10 +``` + +## log₂(e) + +Convert from a natural logarithm to a base-2 logarithm. + +```block +Math._constant(Math.LOG2E) +``` + +The log₂(e) constant is equal to ln(e) / ln(2) which is also 1 / ln(2). + +## log₁₀(e) + +Convert from a natural logarithm to a base-10 logarithm. + +```block +Math._constant(Math.LOG10E) +``` + +The log₁₀(e) constant is equal to 1 / ln(10) which is also 1 / ln(10). + +## √ÂŊ + +The square root of one half (1/2). + +```block +Math._constant(Math.SQRT1_2) +``` + +### Example + +Find the length of the sides of a square with a diagonal length of 9. + +```block +let side = 9 * Math.SQRT1_2 +``` + +## √2 + +The square root of 2. + +```block +Math._constant(Math.SQRT2) +``` + +### Example + +Find out how much shorter it is by walking across a square parking lot on a street corner than using the sidewalk on the sides. Each side of the parking lot is 50 meters. + +```block +let walk_diff = 2 * 50 - 50 * Math.SQRT2 +``` diff --git a/docs/reference/math/convert.md b/docs/reference/math/convert.md new file mode 100644 index 00000000000..c31562a09f3 --- /dev/null +++ b/docs/reference/math/convert.md @@ -0,0 +1,39 @@ +# convert + +Converts a value from one unit to another. + +```sig +Math.convert(0, UnitConversion.DegreesToRadians) +``` + +## Parameters + +* **value**: a [number](/types/number) that is the value to convert to the new units. +* **type**: the unit type to convert to. The units available are: + +>* `degrees to radians` - change from angle units of degrees to angle units of radians +>* `radians to degrees` - change from angle units of radians to angle units of degrees +>* `celsius to fahrenheit` - change from temperature units of degrees celsius to degrees fahrenheit +>* `fahrenheit to celsius` - change from temperature units of degrees fahrenheit to degrees celsius + +## Returns + +* a [number](/types/number) that is the **value** converted to a new value with the selected units. + +## Example + +Show what the measured temperature is every 30 seconds. Use degrees fahrenheit for the display. + +```blocks +let tempc = 0 +let tempf = 0 +loops.everyInterval(500, function () { + tempc = input.temperature() + tempf = Math.convert(tempc, UnitConversion.CelsiusToFahrenheit) + basic.showNumber(tempf) +}) +``` + +## See Also + +[random int](/reference/math/randint) \ No newline at end of file diff --git a/docs/reference/math/random-boolean.md b/docs/reference/math/random-boolean.md new file mode 100644 index 00000000000..229e5e758af --- /dev/null +++ b/docs/reference/math/random-boolean.md @@ -0,0 +1,39 @@ +# random Boolean + +Returns a pseudo-random boolean value that is either `true` or `false`. + +```sig +Math.randomBoolean() +``` + +## Returns + +* a pseudo-random [boolean](types/boolean) that is either `true` or `false`. + +### ~ hint + +#### What is pseudo-random? + +Random numbers generated on a computer are often called pseudo-random. This because the method to create the number is based on some starting value obtained from the computer itself. The formula for the random number could use some amount of mathematical operations on a value derived from a timer or some other input. The resulting "random" number isn’t considered entirely random because it started with some initial value and a repeatable set of operations on it. Therefore, it’s called a pseudo-random number. + +A random boolean is created by choosing a [random int](/reference/math/randint) ranging from `0` to `1`. + +### ~ + +## Example + +Make your @boardname@ do a coin toss when it's dropped softly. Have the LEDs show 'heads' or 'tails' as the result of the toss. + +```blocks +input.onGesture(Gesture.FreeFall, () => { + if (Math.randomBoolean()) { + basic.showIcon(IconNames.Happy) + } else { + basic.showIcon(IconNames.Sword) + } +}) +``` + +## See Also + +[random int](/reference/math/randint) \ No newline at end of file diff --git a/docs/reference/music.md b/docs/reference/music.md index 02be446dfbb..4a223b7e893 100644 --- a/docs/reference/music.md +++ b/docs/reference/music.md @@ -24,4 +24,8 @@ music.volume() [stopMelody](/reference/music/stop-melody), [onEvent](/reference/music/on-event), [beat](/reference/music/beat), [tempo](/reference/music/tempo), [changeTempoBy](/reference/music/change-tempo-by), [setTempo](/reference/music/set-tempo), -[setVolume](/reference/music/set-volume), [volume](/reference/music/volume) +[setVolume](/reference/music/set-volume), [volume](/reference/music/volume), +[play sound effect](/reference/music/play-sound-effect), +[create sound effect](/reference/music/create-sound-effect), +[built-in sound effect](/reference/music/builtin-sound-effect) + diff --git a/docs/reference/music/beat.md b/docs/reference/music/beat.md index dc33bbef91a..56ea3ed490b 100644 --- a/docs/reference/music/beat.md +++ b/docs/reference/music/beat.md @@ -1,17 +1,11 @@ # Beat -Returns the duration of a beat in milli-seconds +Returns the duration of a beat in milliseconds ```sig music.beat(BeatFraction.Whole) ``` -## ~ hint - -**Simulator**: This function only works on the @boardname@ and in some browsers. - -## ~ - ## Parameters * ``BeatFraction`` means fraction of a beat (BeatFraction.Whole, BeatFraction.Sixteenth etc) diff --git a/docs/reference/music/built-in-melody.md b/docs/reference/music/built-in-melody.md new file mode 100644 index 00000000000..62c7994eb17 --- /dev/null +++ b/docs/reference/music/built-in-melody.md @@ -0,0 +1,51 @@ +# built In Melody + +Get a melody string for a built-in melody. + +```sig +music.builtInMelody(Melodies.Dadadadum) +``` + +A collection of built-in melodies are available. You choose one by selecting the name of the melody. + +## Parameters + +* **melody**: A melody name. The available melodies are: + +>* `dadadum` +>* `entertainer` +>* `prelude` +>* `ode` +>* `nyan` +>* `ringtone` +>* `funk` +>* `blues` +>* `birthday` +>* `wedding` +>* `funeral` +>* `punchline` +>* `baddy` +>* `chase` +>* `ba ding` +>* `wawawawaa` +>* `jump up` +>* `jump down` +>* `power up` +>* `power down` + +## Returns + +* a [string](/types/string) that contains the melody. + +## Example + +Play the built-in melody for **blues**. + +```blocks +music.startMelody(music.builtInMelody(Melodies.Blues), MelodyOptions.Once) +``` + +## See also + +[start melody](/reference/music/start-melody), +[built-in sound effect](/reference/music/builtin-sound-effect) \ No newline at end of file diff --git a/docs/reference/music/built-in-playable-melody.md b/docs/reference/music/built-in-playable-melody.md new file mode 100644 index 00000000000..fb09beb8e65 --- /dev/null +++ b/docs/reference/music/built-in-playable-melody.md @@ -0,0 +1,50 @@ +# built In Playable Melody + +Get a playable sound object for a built-in melody. + +```sig +music.builtInPlayableMelody(Melodies.Dadadadum) +``` + +A collection of built-in melodies are available as [playable](/types/playable) sound objects. You choose one by selecting the name of the melody. + +## Parameters + +* **melody**: A melody name. The available melodies are: + +>* `dadadum` +>* `entertainer` +>* `prelude` +>* `ode` +>* `nyan` +>* `ringtone` +>* `funk` +>* `blues` +>* `birthday` +>* `wedding` +>* `funeral` +>* `punchline` +>* `baddy` +>* `chase` +>* `ba ding` +>* `wawawawaa` +>* `jump up` +>* `jump down` +>* `power up` +>* `power down` + +## Returns + +* a [playable](/types/playable) object that contains the melody. + +## Example + +Play the built-in melody for **blues**. + +```blocks +music.play(music.builtInPlayableMelody(Melodies.Blues), music.PlaybackMode.InBackground) +``` + +## See also + +[string playable](/reference/music/string-playable) \ No newline at end of file diff --git a/docs/reference/music/builtin-sound-effect.md b/docs/reference/music/builtin-sound-effect.md new file mode 100644 index 00000000000..160d6b7280d --- /dev/null +++ b/docs/reference/music/builtin-sound-effect.md @@ -0,0 +1,40 @@ +# builtin Sound Effect + +Get a sound expression string for a built-in sound effect. + +```sig +music.builtinSoundEffect(soundExpression.giggle) +``` + +A collection of built-in sound effects are available as sound expressions. You choose one by selecting the name of the effect + +## Parameters + +* **soundExpression**: A sound expression name. The available effects are: +>* `giggle` +>* `happy` +>* `hello` +>* `mysterious` +>* `sad` +>* `slide` +>* `soaring` +>* `spring` +>* `twinkle` +>* `yawn` + +## Returns + +* a [sound](/types/sound) expression [string](/types/string) with the the named sound effect. + +## Example + +Play the built-in sound effect for `giggle`. + +```blocks +music.playSoundEffect(music.builtinSoundEffect(soundExpression.giggle), SoundExpressionPlayMode.UntilDone) +``` + +## See also + +[play sound effect](/reference/music/play-sound-effect), +[create sound effect](/reference/music/create-sound-effect) \ No newline at end of file diff --git a/docs/reference/music/change-tempo-by.md b/docs/reference/music/change-tempo-by.md index 1e646a26b68..97d32319725 100644 --- a/docs/reference/music/change-tempo-by.md +++ b/docs/reference/music/change-tempo-by.md @@ -7,11 +7,13 @@ faster or slower by the amount you say. music.changeTempoBy(20) ``` -## ~ hint +### ~hint -**Simulator**: This function only works on the @boardname@ and in some browsers. +#### Simulator -## ~ +``||music:change tempo by||`` works on the @boardname@. It might not work in the simulator on every browser. + +### ~ ## Parameters diff --git a/docs/reference/music/create-sound-effect.md b/docs/reference/music/create-sound-effect.md new file mode 100644 index 00000000000..9d09ebb4f23 --- /dev/null +++ b/docs/reference/music/create-sound-effect.md @@ -0,0 +1,68 @@ +# create Sound Effect + +Create a sound expression string for a sound effect. + +```sig +music.createSoundEffect(WaveShape.Sine, 2000, 0, 1023, 0, 500, SoundExpressionEffect.None, InterpolationCurve.Linear) +``` + +A sound expression is set of parameters that describe a **[Sound](/types/sound)** that will last for some amount of time. These parameters specify a base waveform, frequency range, sound volume, and effects. Sound data is created as a [Sound](/types/sound) object and can then be [played](/reference/music/play-sound-effect) to the speaker, headphones, or at an output pin. + +## Parameters + +* **waveShape**: the primary shape of the waveform: +>* `sine`: sine wave shape +>* `sawtooth`: sawtooth wave shape +>* `triangle`: triangle wave shape +>* `square`: square wave shape +>* `noise`: random noise generated wave shape +* **startFrequency**: a [number](/types/number) that is the frequency of the waveform when the sound expression starts. +* **endFrequency**: a [number](/types/number) that is the frequency of the waveform when the sound expression stops. +* **startVolume**: a [number](/types/number) the initial volume of the sound expression. +* **endVolume**: a [number](/types/number) the ending volume of the sound expression. +* **duration**: a [number](/types/number) the duration in milliseconds of the sound expression. +* **effect**: an effect to add to the waveform. These are: +>* `tremolo`: add slight changes in volume of the sound expression. +>* `vibrato`: add slight changes in frequency to the sound expression. +>* `warble`: similar to `vibrato` but with faster variations in the frequency changes. +* **interpolation**: controls the rate of frequency change in the sound expression. +>* `linear`: the change in frequency is constant for the duration of the sound. +>* `curve`: the change in frequency is faster at the beginning of the sound and slows toward the end. +>* `logarithmic`: the change in frequency is rapid during the very first part of the sound. + +## Returns + +* a [sound](/types/sound) expression [string](/types/string) with the the desired sound effect parameters. + +## Examples + +### Sine wave sound + +Create a sound expression string and assign it to a variable. Play the sound for the sound expression. + +```blocks +let mySound = music.createSoundEffect(WaveShape.Sine, 2000, 0, 1023, 0, 500, SoundExpressionEffect.None, InterpolationCurve.Linear) +music.playSoundEffect(mySound, SoundExpressionPlayMode.UntilDone) +``` + +### Complex waveform sound + +Create a `triangle` wave sound expression with `vibrato` and a `curve` interpolation. Play the sound until it finishes. + +```typescript +let mySound = music.createSoundEffect( + WaveShape.Triangle, + 1000, + 2700, + 255, + 255, + 500, + SoundExpressionEffect.Vibrato, + InterpolationCurve.Curve + ) +music.playSoundEffect(mySound, SoundExpressionPlayMode.UntilDone) +``` + +## See also + +[play sound effect](/reference/music/play-sound-effect), [built-in sound effect](/reference/music/builtin-sound-effect) diff --git a/docs/reference/music/create-sound-expression.md b/docs/reference/music/create-sound-expression.md new file mode 100644 index 00000000000..73a80060b10 --- /dev/null +++ b/docs/reference/music/create-sound-expression.md @@ -0,0 +1,68 @@ +# create Sound Expression + +Create a sound expression object for a sound effect. + +```sig +music.createSoundExpression(WaveShape.Sine, 2000, 0, 1023, 0, 500, SoundExpressionEffect.None, InterpolationCurve.Linear) +``` + +A sound expression is set of parameters that describe a **[Sound](/types/sound)** that will last for some amount of time. These parameters specify a base waveform, frequency range, sound volume, and effects. Sound data is created as a [Sound](/types/sound) object and can then be [played](/reference/music/play) to the speaker, headphones, or at an output pin. + +## Parameters + +* **waveShape**: the primary shape of the waveform: +>* `sine`: sine wave shape +>* `sawtooth`: sawtooth wave shape +>* `triangle`: triangle wave shape +>* `square`: square wave shape +>* `noise`: random noise generated wave shape +* **startFrequency**: a [number](/types/number) that is the frequency of the waveform when the sound expression starts. +* **endFrequency**: a [number](/types/number) that is the frequency of the waveform when the sound expression stops. +* **startVolume**: a [number](/types/number) the initial volume of the sound expression. +* **endVolume**: a [number](/types/number) the ending volume of the sound expression. +* **duration**: a [number](/types/number) the duration in milliseconds of the sound expression. +* **effect**: an effect to add to the waveform. These are: +>* `tremolo`: add slight changes in volume of the sound expression. +>* `vibrato`: add slight changes in frequency to the sound expression. +>* `warble`: a combination of the `tremolo` and `vibrato` effects. +* **interpolation**: controls the rate of frequency change in the sound expression. +>* `linear`: the change in frequency is constant for the duration of the sound. +>* `curve`: the change in frequency is faster at the beginning of the sound and slows toward the end. +>* `logarithmic`: the change in frequency is rapid during the very first part of the sound. + +## Returns + +* a [sound](/types/sound) expression with the the desired sound effect parameters. + +## Examples + +### Sine wave sound + +Create a sound expression and assign it to a variable. Play the sound for the sound expression. + +```blocks +let mySound = music.createSoundExpression(WaveShape.Sine, 2000, 0, 1023, 0, 500, SoundExpressionEffect.None, InterpolationCurve.Linear) +music.play(mySound, music.PlaybackMode.UntilDone) +``` + +### Complex waveform sound + +Create a `triangle` wave sound expression with `vibrato` and a `curve` interpolation. Play the sound until it finishes. + +```typescript +let mySound = music.createSoundExpression( + WaveShape.Triangle, + 1000, + 2700, + 255, + 255, + 500, + SoundExpressionEffect.Vibrato, + InterpolationCurve.Curve + ) +music.play(mySound, music.PlaybackMode.UntilDone) +``` + +## See also + +[play](/reference/music/play) diff --git a/docs/reference/music/is-sound-playing.md b/docs/reference/music/is-sound-playing.md new file mode 100644 index 00000000000..581564f3b9f --- /dev/null +++ b/docs/reference/music/is-sound-playing.md @@ -0,0 +1,40 @@ +# is Sound Playing + +Check if sound is playing at any sound output. + +```sig +music.isSoundPlaying() +``` + +### ~ reminder + +![works with micro:bit V2 only image](/static/v2/v2-only.png) + +This function requires the [micro:bit V2](/device/v2) hardware. If you use this function with a micro:bit v1 board, you will see the **927** error code on the screen. + +### ~ + +Sound is played at the built-in speaker or at the selected audio output pin. You can check if any sound is currently being played at any of these outputs. + +## Returns + +* a [boolean](/types/boolean) value that is `true` if sound is being played at the built-in speaker or at the audio pin. The value is `false` otherwise. + +## Example #example + +Stop all sounds if any are currently playing. + +```blocks +if (music.isSoundPlaying()) { + music.stopAllSounds() +} +``` + +## See also + +[set built-in speaker enabled](/reference/music/set-built-in-speaker-enabled), +[set audio pin](/reference/pins/set-audio-pin) + +```package +music +``` diff --git a/docs/reference/music/note-frequency.md b/docs/reference/music/note-frequency.md new file mode 100644 index 00000000000..9b9d21bbccf --- /dev/null +++ b/docs/reference/music/note-frequency.md @@ -0,0 +1,31 @@ +# note Frequency + +Get the frequency of a musical note. + +```sig +music.noteFrequency(Note.C) +``` + +## Parameters + +* ``name`` is the name of the **Note** you want a frequency value for. + +## Returns + +* a [number](/types/number) that is the frequency (in [Hertz](https://wikipedia.org/wiki/Hertz)) +of a note you chose. + +## Example #example + +Play a 'C' note for one second, rest for one second, and then play an 'A' note for one second. + +```blocks +music.playTone(music.noteFrequency(Note.C), 1000) +music.rest(1000) +music.playTone(music.noteFrequency(Note.A), 1000) +``` +## See also #seealso + +[play tone](/reference/music/play-tone), [ring tone](/reference/music/ring-tone), +[rest](/reference/music/rest), [tempo](/reference/music/tempo), +[change tempo by](/reference/music/change-tempo-by) diff --git a/docs/reference/music/on-event.md b/docs/reference/music/on-event.md index b6c0e7d0012..366ab2ded41 100644 --- a/docs/reference/music/on-event.md +++ b/docs/reference/music/on-event.md @@ -60,6 +60,6 @@ control.inBackground(function () { music.beginMelody(music.builtInMelody(Melodies.Entertainer), MelodyOptions.Once) }) music.onEvent(MusicEvent.BackgroundMelodyStarted, function () { - basic.showIcon(IconNames.EigthNote) + basic.showIcon(IconNames.EighthNote) }) ``` diff --git a/docs/reference/music/play-melody.md b/docs/reference/music/play-melody.md new file mode 100644 index 00000000000..1d9df3910f0 --- /dev/null +++ b/docs/reference/music/play-melody.md @@ -0,0 +1,38 @@ +# play Melody + +Play a short melody of notes composed in a string. + +```sig +music.playMelody("", 120); +``` + +The melody is short series of notes composed in a string. The melody is played at a rate set by the **tempo** value you give. The melody string contains a sequence of notes formatted like this: + +``"E B C5 A B G A F "`` + +The melody is shown in the ``||music:play melody||`` block as note symbols which also appear in the Melody Editor. + +```block +music.playMelody("E B C5 A B G A F ", 120); +``` + +The melodies are most often created in the Melody Editor from the block so that valid notes are chosen and the correct melody length is set. + +## Parameters + +* **melody**: a [string](/types/string) which contains the notes of the melody. +* **tempo**: a [number](/types/number) which is the rate to play the melody at in beats per minute. + +## Example #example + +Play the ``Mystery`` melody continuously. + +```blocks +basic.forever(function () { + music.playMelody("E F G F E G B C5 ", 120) +}) +``` + +## See also #seealso + +[set tempo](/reference/music/set-tempo), [play](/reference/music/play), [play until done](/reference/music/play-until-done) diff --git a/docs/reference/music/play-sound-effect.md b/docs/reference/music/play-sound-effect.md new file mode 100644 index 00000000000..1a2c2737a2b --- /dev/null +++ b/docs/reference/music/play-sound-effect.md @@ -0,0 +1,71 @@ +# play Sound Effect + +Play a sound that is generated from a sound expression. + +```sig +music.playSoundEffect("", SoundExpressionPlayMode.UntilDone) +``` + +This will play a **[Sound](/types/sound)** object created from a sound expression. The sound will play for the duration that was set in the sound expression. The sound can play on the speaker or at a pin that is set for sound output. + +You can also play [built-in sound effects](/reference/music/builtin-sound-effect) like `giggle`, `happy`, or `twinkle`. + +Your program can wait for the sound to finish before it runs its next step. To do this, set the play mode to `until done`. Otherwise, use `background` for the program to continue immediately after the sound starts. + +### ~ reminder + +#### Works with micro:bit V2 + +![works with micro:bit V2 only image](/static/v2/v2-only.png) + +This block requires the [micro:bit V2](/device/v2) hardware. If you use this block with a micro:bit v1 board, you will see the **927** error code on the screen. + +### ~ + +## Parameters + +* **sound**: a [string](/types/string) that is the sound expression for the sound you want to play. +* **mode**: the play mode for the sound, either `until done` or `background`. + +## Examples + +### Simple waveform sound + +Play the sound effect from a sine wave sound expression for `1` second. + +```blocks +music.playSoundEffect(music.createSoundEffect(WaveShape.Sine, 2000, 0, 1023, 0, 1000, SoundExpressionEffect.None, InterpolationCurve.Linear), SoundExpressionPlayMode.UntilDone) +``` + +### Complex waveform sound + +Play a `triangle` wave sound effect with `vibrato` and a `curve` interpolation. + +```typescript +music.playSoundEffect(music.createSoundEffect( + WaveShape.Triangle, + 1000, + 2700, + 255, + 255, + 500, + SoundExpressionEffect.Vibrato, + InterpolationCurve.Curve + ), SoundExpressionPlayMode.UntilDone) +``` + +### Built-in sounds + +Play the `giggle` [built-in sound effect](/reference/music/builtin-sound-effect) until it finishes. + +```blocks +music.playSoundEffect(music.builtinSoundEffect(soundExpression.giggle), SoundExpressionPlayMode.UntilDone) + +``` + +## See also + +[create sound effect](/reference/music/create-sound-effect), +[built-in sound effect](/reference/music/builtin-sound-effect), +[set built in speaker enabled](/reference/music/set-built-in-speaker-enabled), +[analog set pitch pin](/reference/pins/analog-set-pitch-pin) diff --git a/docs/reference/music/play-tone.md b/docs/reference/music/play-tone.md index b9cd9224560..2da096cf9d5 100644 --- a/docs/reference/music/play-tone.md +++ b/docs/reference/music/play-tone.md @@ -1,39 +1,52 @@ # Play Tone -Play a musical tone through pin ``P0`` of the @boardname@ for as long as you say. - -## ~ hint - -This function only works on the @boardname@ and in some browsers. - -## ~ +Play a musical tone on the speaker or at a sound pin of the @boardname@ for as long as you say. ```sig music.playTone(440, 120) ``` +The frequency of the tone is set as a number of cycle per second, or Hertz. The [note frequency](/reference/music/note-frequency) block will allow you to use a musical note for the tone instead of a number of Hertz. + +The duration of the tone is set as a number of milliseconds. It's typical though to use a number of beats or a beat fraction for the tone duration instead. The ``||music:beat||`` block is used to convert beats to milliseconds. You can also make a custom duration by just setting the tone duration to certain amount of milliseconds. + +### ~hint + +#### Simulator + +The ``||music:play tone||`` block works on the @boardname@ board. It might not work in the simulator on every browser. + +### ~ ## Parameters -* ``frequency`` is the [number](/types/number) of Hertz (how high or low the tone is). -* ``ms`` is the [number](/types/number) of milliseconds that the tone lasts +* **frequency** is the [number](/types/number) of Hertz (how high or low the tone is). You can set this value with a note instead by using the [note frequency](/reference/music/note-frequency) block. +* **ms** is the [number](/types/number) of milliseconds for the duration of the tone. A [beat](/reference/music/beat) value is used instead as the block's default tone duration. The number of beats is converted to milliseconds for you. + ## Example -This example stores the musical note C in the variable `freq`. -Next, it plays that note for 1000 milliseconds (one second). +### Tone and beat + +Play a `Middle C` for `1 beat`. ```blocks -let freq = music.noteFrequency(Note.C) -music.playTone(freq, 1000) +music.playTone(music.noteFrequency(Note.C), music.beat(BeatFraction.Whole)) ``` +### Custom tone frequency and duration + +Play a `250` Hertz tone for `1000` milliseconds. + +```blocks +music.playTone(250, 1000) +``` ## Using other pins -Use [analogSetPitchPin](/reference/pins/analog-set-pitch-pin) to change that pin used to generate music. +Use [analogSetPitchPin](/reference/pins/analog-set-pitch-pin) to change the pin used to generate music. ```blocks -pins.analogSetPitchPin(AnalogPin.P1); +pins.analogSetPitchPin(AnalogPin.P1) ``` ## See also diff --git a/docs/reference/music/play.md b/docs/reference/music/play.md index 4b2ff1dd941..8c7a6e3e227 100644 --- a/docs/reference/music/play.md +++ b/docs/reference/music/play.md @@ -1,43 +1,110 @@ # play -Play a sound expression. +Play a song, melody, tone, or a sound effect from a playable music source. ```sig -soundExpression.giggle.play() +music.play(music.tonePlayable(262, music.beat(BeatFraction.Whole)), music.PlaybackMode.UntilDone) ``` -A sound expression is a preformatted set of tones that create a certain sound. There are several sounds to choose from. The sound is started and your program then continues. +Music is played for a simple tone, a melody, or a song. Each of these music sources is called a [playable](/types/playable) object. The ``||music:play||`` block can take any of these playable objects and play them as sound output for your game. ### ~ reminder +#### For micro:bit v2 only + ![works with micro:bit V2 only image](/static/v2/v2-only.png) This block requires the [micro:bit V2](/device/v2) hardware. If you use this block with a micro:bit v1 board, you will see the **927** error code on the screen. ### ~ -## Parameters +The simplest music source is a **tone**, on note play for a duration of time: + +```block +music.play(music.tonePlayable(262, music.beat(BeatFraction.Whole)), music.PlaybackMode.UntilDone) +``` -In blocks, the sound is selected from the list in the ``||music:play sound||`` block. +Then, there is the **melody** which is a series of notes played at a certain speed, or `tempo`. You can create your own melody of choose a built-in one to play: ```block -soundExpression.giggle.play() +music.play(music.stringPlayable("D F E A E A C B ", 120), music.PlaybackMode.UntilDone) +music.play(music.builtInPlayableMelody(Melodies.BaDing), music.PlaybackMode.UntilDone) ``` -When coding in JavaScript or Python, the sound is a ``soundExpression`` object which from which you run the ``play()`` function from. For example, to play the ``soaring`` sound, select ``soaring`` from the ``soundExpression`` namespace and run ``play()``: +The most complex playable object is a **sound expression**. [Sound expressions](/reference/music/create-sound-expression) are composed in the [Sound Editor](/types/sound#sound-editing) using different parameters for making sound waves and effects.. -```typescript -soundExpression.soaring.play() +```block +music.play(music.createSoundExpression(WaveShape.Sine, 5000, 0, 255, 0, 500, SoundExpressionEffect.None, InterpolationCurve.Linear), music.PlaybackMode.UntilDone) ``` -## Example +## Parameters + +* **toPlay**: the [playable](/types/playable) object, or music source, to play. +* **playbackMode**: the playback mode for continuing the program: +>* `play until done`: play the music source in **toPlay** but wait to run the next part of the program until music play is done. +>* `in background`: play the music source in **toPlay** but continue with the rest of the program before music play is done. +>* `looping in background`: play the music source in **toPlay** but continue with the rest of the program before music play is done. The music will remain playing, returning to the first note of the music after its duration. + +### ~ hint + +#### Stop the music! + +You can stop any music currently playing with the ``||music:stop all sounds||`` block. This is useful if **playbackMode** is set to `in background looping` and you wish to stop the music for a scene change or respond to an event with a different sound. + +### ~ + +## Examples #example + +### Play a melody + +Play a short melody created in the Melody Editor. + +```blocks +music.play(music.stringPlayable("D F E A E A C B ", 120), music.PlaybackMode.UntilDone) +``` + +### Different music sources, one block to play them all + +Put 4 different playable music sources in an array. Play one after the other. + +```blocks +let playables = [ +music.tonePlayable(262, music.beat(BeatFraction.Whole)), +music.stringPlayable("D F E A E A C B ", 120), +music.builtInPlayableMelody(Melodies.BaDing), +music.createSoundExpression(WaveShape.Sine, 5000, 0, 255, 0, 500, SoundExpressionEffect.None, InterpolationCurve.Linear) +] +for (let someMusic of playables) { + music.play(someMusic, music.PlaybackMode.UntilDone) + basic.pause(500) +} +``` + +### Looping music play + +Play a simple song in the background. When the @boardname@ is shaken, stop the song an play the `power down` melody. + +```blocks +music.play(music.stringPlayable("C5 A B G A F A C5 ", 120), music.PlaybackMode.LoopingInBackground) +input.onGesture(Gesture.Shake, function () { + music.stopAllSounds() + music.play(music.builtInPlayableMelody(Melodies.PowerDown), music.PlaybackMode.InBackground) +}) +``` +### Play a sound effect -Play the ``twinkle`` sound on the speaker. +Play a sine wave sound effect for `5` seconds. ```blocks -soundExpression.twinkle.play() +music.play(music.createSoundExpression(WaveShape.Sine, 5000, 0, 255, 0, 5000, SoundExpressionEffect.None, InterpolationCurve.Linear), music.PlaybackMode.UntilDone) ``` ## See also -[play until done](/reference/music/play-until-done) \ No newline at end of file +[tone playable](/reference/music/tone-playable), +[string playable](/reference/music/string-playable), +[melody playable](/reference/music/built-in-melody-playable), +[create song](/reference/music/create-song), +[stop all sounds](/reference/music/stop-all-sounds), +[sound editor](/reference/types/sound#sound-editing), +[create sound expression](/reference/music/create-sound-expression) \ No newline at end of file diff --git a/docs/reference/music/rest.md b/docs/reference/music/rest.md index 019e5ccc5ad..e6f703b674a 100644 --- a/docs/reference/music/rest.md +++ b/docs/reference/music/rest.md @@ -1,29 +1,47 @@ # Rest -Rest (play no sound) through pin `PO` for the amount of time you say. +Play no sound (rest) on the speaker or at a sound pin for the amount of time you say. ```sig music.rest(400) ``` -### ~ hint +The duration of the rest is set as a number milliseconds. Instead, it's typical to use a number of beats or a beat fraction for a rest. The ``||music:beat||`` block is used to convert beats to milliseconds. You can also make a custom rest by setting the rest duration to certain amount of milliseconds. -**Simulator**: This function only works on the @boardname@ and in some browsers. +### ~hint -## ~ +#### Simulator + +The ``||music:rest||`` block works on the @boardname@ board. It might not work in the simulator on every browser. + +### ~ ## Parameters -* ``ms`` is a [number](/types/number) saying how many - milliseconds the @boardname@ should rest. One second is 1000 - milliseconds. +* **ms** is the [number](/types/number) of milliseconds for the duration of the rest. A [beat](/reference/music/beat) value is used instead as the block's default rest duration. The number of beats is converted to milliseconds for you. ## Example +### Middle C loop + +Continuously play a `Middle C` tone for `1` beat and rest for `2` beats. + +```blocks +basic.forever(function () { + music.playTone(262, music.beat(BeatFraction.Whole)) + music.rest(music.beat(BeatFraction.Double)) +}) +``` + +### Custom rest time + +Continuously play a `Middle C` note followed by a random rest time. + ```blocks -let frequency = music.noteFrequency(Note.C) -music.playTone(frequency, 1000) -music.rest(1000) +basic.forever(function () { + music.playTone(262, music.beat(BeatFraction.Whole)) + music.rest(randint(500, 2000)) +}) ``` ## See also diff --git a/docs/reference/music/ring-tone.md b/docs/reference/music/ring-tone.md index fd5d06d42a8..2a2e0412b5e 100644 --- a/docs/reference/music/ring-tone.md +++ b/docs/reference/music/ring-tone.md @@ -1,22 +1,23 @@ # Ring Tone -Play a musical tone through pin `P0` with the pitch as high or low as you say. -The tone will keep playing until you tell it not to. +Play a musical tone on the speaker or at a sound pin of the @boardname@ with the pitch as high or low as you say. The tone will keep playing until you tell it not to. ```sig music.ringTone(440) ``` -## ~ hint +### ~hint -**Simulator**: This function only works on the @boardname@ and in some browsers. +#### Simulator -## ~ +The ``||music:ring tone||`` block works on the @boardname@ board. It might not work in the simulator on every browser. + +### ~ ## Parameters * ``frequency`` is a [number](/types/number) that says -how high-pitched or low-pitched the tone is. This +how high-pitched or low-pitched the tone is. This number is in **Hz** (**Hertz**), which is a measurement of frequency or pitch. diff --git a/docs/reference/music/set-tempo.md b/docs/reference/music/set-tempo.md index a2b31f1aa82..b14a0706251 100644 --- a/docs/reference/music/set-tempo.md +++ b/docs/reference/music/set-tempo.md @@ -5,11 +5,15 @@ Makes the tempo (speed of a piece of music) as fast or slow as you say. ```sig music.setTempo(60) ``` -## ~ hint -**Simulator**: This function only works on the @boardname@ and in some browsers. +### ~hint + +#### Simulator + +``||music:set tempo||`` works on the @boardname@. It might not work in the simulator on every browser. + +### ~ -## ~ ## Parameters diff --git a/docs/reference/music/stop-all-sounds.md b/docs/reference/music/stop-all-sounds.md new file mode 100644 index 00000000000..e2e6d3dc036 --- /dev/null +++ b/docs/reference/music/stop-all-sounds.md @@ -0,0 +1,34 @@ +# stop All Sounds + +Stop all the sounds that are playing right now and any others waiting to play. + +```sig +music.stopAllSounds() +``` + +If you play sounds or sound effects more than once, the sounds you asked to play later have to wait until the sounds played earlier finish. You can stop the sound that is playing now and all the sounds waiting to play with ``||music:stop all sounds||``. + +## #simnote + +### ~hint + +#### Simulator + +``||music:stop all sounds||`` works on the @boardname@. It might not work in the simulator on every browser. + +### ~ + +## Example #example + +Play a tone but stop it right away. + +```blocks +let freq = music.noteFrequency(Note.C); +music.playTone(freq, 1000) +music.stopAllSounds() +``` + +## See also #seealso + +[play melody](/reference/music/play-melody), [play](/reference/music/play), +[play tone](/reference/music/play-tone) diff --git a/docs/reference/music/string-playable.md b/docs/reference/music/string-playable.md new file mode 100644 index 00000000000..558dfeab13f --- /dev/null +++ b/docs/reference/music/string-playable.md @@ -0,0 +1,40 @@ +# string Playable + +Created a short melody of notes composed in a string. + +```sig +music.stringPlayable("D F E A E A C B ", 120) +``` + +The **melody** is short series of notes composed in a string. The melody is played at a rate set by the **tempo** value you give. The melody string contains a sequence of notes formatted like this: + +``"E B C5 A B G A F "`` + +The melody is shown in the ``||music:melody||`` block as note symbols which also appear in the Melody Editor. + +```block +music.stringPlayable("E F G F E G B C5 ", 120) +``` + +The melodies are most often created in the Melody Editor from the block so that valid notes are chosen and the correct melody length is set. + +## Parameters + +* **melody**: a [string](/types/string) which contains the notes of the melody. +* **tempo**: a [number](/types/number) which is the rate to play the melody at in beats per minute. + +## Returns + +* a [playable](/types/playable) object that contains the **melody** and **tempo**. + +## Example + +Play the ``Mystery`` melody continuously. + +```blocks +music.play(music.stringPlayable("E F G F E G B C5 ", 120), music.PlaybackMode.LoopingInBackground) +``` + +## See also + +[tone playable](/reference/music/tone-playable) \ No newline at end of file diff --git a/docs/reference/music/tone-playable.md b/docs/reference/music/tone-playable.md new file mode 100644 index 00000000000..cf3742abc61 --- /dev/null +++ b/docs/reference/music/tone-playable.md @@ -0,0 +1,29 @@ +# tone Playable + +Create a musical tone that will play for some amount of time. + +```sig +music.tonePlayable(262, music.beat(BeatFraction.Whole)) +``` + +## Parameters + +* **note**: is the note frequency as a [number](/types/number) of [Hertz](https://wikipedia.org/wiki/Hertz) (how high or low the tone is, also known as _pitch_). If **note** is less or equal to zero, no sound is played. +* **duration**: is the [number](/types/number) of milliseconds (one-thousandth of a second) that the tone lasts for. If **duration** is negative or zero, the sound will play continuously. + +## Returns + +* a [playable](/types/playable) object that contains the tone. + +## Example + +Store the musical note 'C' in the variable `note` and play that note for 1000 milliseconds (one second). + +```blocks +let note = music.noteFrequency(Note.C); +music.play(music.tonePlayable(note, music.beat(BeatFraction.Whole)), music.PlaybackMode.UntilDone) +``` + +## See also + +[string playable](/reference/music/string-playable) \ No newline at end of file diff --git a/docs/reference/pins.md b/docs/reference/pins.md index ae0305d5eae..b207511ad2d 100644 --- a/docs/reference/pins.md +++ b/docs/reference/pins.md @@ -44,5 +44,5 @@ pins.spiPins(DigitalPin.P0, DigitalPin.P1, DigitalPin.P2); ## See Also -[digitalReadPin](/reference/pins/digital-read-pin), [digitalWritePin](/reference/pins/digital-write-pin), [analogReadPin](/reference/pins/analog-read-pin), [analogWritePin](/reference/pins/analog-write-pin), [analogSetPeriod](/reference/pins/analog-set-period), [map](/reference/pins/map), [onPulsed](/reference/pins/on-pulsed), [pulseDuration](/reference/pins/pulse-duration), [pulseIn](/reference/pins/pulse-in), [servoWritePin](/reference/pins/servo-write-pin), [servoSetPulse](/reference/pins/servo-set-pulse), [i2cReadNumber](/reference/pins/i2c-read-number), [i2cWriteNumber](/reference/pins/i2c-write-number), [setPull](/reference/pins/set-pull), [analogPitch](/reference/pins/analog-pitch), [analogSetPitchPin](/reference/pins/analog-set-pitch-pin), [spiWrite](/reference/pins/spi-write), +[digitalReadPin](/reference/pins/digital-read-pin), [digitalWritePin](/reference/pins/digital-write-pin), [analogReadPin](/reference/pins/analog-read-pin), [analogWritePin](/reference/pins/analog-write-pin), [analogSetPeriod](/reference/pins/analog-set-period), [map](/reference/pins/map), [onPulsed](/reference/pins/on-pulsed), [pulseDuration](/reference/pins/pulse-duration), [pulseIn](/reference/pins/pulse-in), [servoWritePin](/reference/pins/servo-write-pin), [servoSetPulse](/reference/pins/servo-set-pulse), [i2cReadNumber](/reference/pins/i2c-read-number), [i2cWriteNumber](/reference/pins/i2c-write-number), [i2cReadBuffer](/reference/pins/i2c-read-buffer), [i2cWriteBuffer](/reference/pins/i2c-write-buffer), [setPull](/reference/pins/set-pull), [analogPitch](/reference/pins/analog-pitch), [analogSetPitchPin](/reference/pins/analog-set-pitch-pin), [spiWrite](/reference/pins/spi-write), [spiPins](/reference/pins/spi-pins),[spiFormat](/reference/pins/spi-format),[spiFrequency](/reference/pins/spi-frequency) diff --git a/docs/reference/pins/analog-pin.md b/docs/reference/pins/analog-pin.md new file mode 100644 index 00000000000..01f7b6c6dc2 --- /dev/null +++ b/docs/reference/pins/analog-pin.md @@ -0,0 +1,32 @@ +# analog Pin + +Get an analog pin number for a pin identifier. + +```sig +pins._analogPin(AnalogPin.P0) +``` + +## Parameters + +* **pin**: a pin identifier for an analog pin (`P0` through `P20`). + +## Returns + +* a pin [number](/types/number) for the pin identifier. + +## Example + +Set an analog pin variable for `P1`, read pin `P1`, and show the input value on the LED screen. + +```blocks +let myPin = AnalogPin.P1 +basic.forever(function() { + let value = pins.analogReadPin(myPin) + basic.showNumber(value) +}) +``` + +## See also + +[analog read pin](/reference/pins/analog-read-pin), +[analog write pin](/reference/pins/analog-write-pin) \ No newline at end of file diff --git a/docs/reference/pins/analog-pitch.md b/docs/reference/pins/analog-pitch.md index 7c201714e2e..0155debdd45 100644 --- a/docs/reference/pins/analog-pitch.md +++ b/docs/reference/pins/analog-pitch.md @@ -1,24 +1,27 @@ # Analog Pitch -Emits a Pulse With Modulation (PWM) signal to the pin ``P0``. -Use [analog set pitch pin](/reference/pins/analog-set-pitch-pin) to set the current pitch pin. +Sends a pulse-width modulation (PWM) signal to the pin ``P0``. ```sig pins.analogPitch(440, 300) ``` +The PWM signal is sent to the current pitch pin. Use [analog set pitch pin](/reference/pins/analog-set-pitch-pin) to first set the current pitch pin. + ## Parameters -* `frequency` : [Number](/types/number) -* `ms`: [Number](/types/number) +* **frequency**: a [number](/types/number) which is the frequency of the PWM signal at the pitch pin. +* **ms**: a [number](/types/number) in milliseconds that is the duration of the signal at the pitch pin. ## Example +Set the pitch pin to `P1` and send a 440 Hz tone for 1 second. + ```blocks -pins.analogSetPitchPin(AnalogPin.P0); +pins.analogSetPitchPin(AnalogPin.P0) let frequency1 = 440 let duration = 1000 -pins.analogSetPitchPin(AnalogPin.P1); +pins.analogSetPitchPin(AnalogPin.P1) pins.analogPitch(frequency1, duration) ``` diff --git a/docs/reference/pins/analog-read-pin.md b/docs/reference/pins/analog-read-pin.md index 21278feeebd..f9911e47a5f 100644 --- a/docs/reference/pins/analog-read-pin.md +++ b/docs/reference/pins/analog-read-pin.md @@ -9,21 +9,23 @@ pins.analogReadPin(AnalogPin.P0) ## Parameters -* ``name`` is a [string](/types/string) with the name of the pin - you say (`P0` through `P4`, or `P10`) +* **name**: is a [string](/types/string) with the name of the pin +you say (`P0` through `P4`, or `P10`). ## Returns * a [number](/types/number) from `0` through `1023` +## Example + This program reads pin `P1` and shows the number on the LED screen. ```blocks -basic.forever(() => { +basic.forever(function() { let value = pins.analogReadPin(AnalogPin.P1) basic.showNumber(value) -}); +}) ``` ### ~hint diff --git a/docs/reference/pins/analog-set-period.md b/docs/reference/pins/analog-set-period.md index e7bb4e84fbb..c2a3e15967f 100644 --- a/docs/reference/pins/analog-set-period.md +++ b/docs/reference/pins/analog-set-period.md @@ -1,6 +1,6 @@ # Analog Set Period -Configure the period of Pulse Width Modulation (PWM) on the specified +Configure the period of pulse-width modulation (PWM) on the specified analog [pin](/device/pins). Before you call this function, you should set the specified pin as analog. @@ -10,8 +10,10 @@ pins.analogSetPeriod(AnalogPin.P0, 20000) ## Parameters -* ``name``: a [string](/types/string) that specifies the pin to configure (`P0` through `P4`, or `P10`) -* ``micros``: a [number](/types/number) that specifies the analog period in microseconds. +* **name**: a [string](/types/string) that specifies the pin to configure (`P0` through `P4`, or `P10`) +* **micros**: a [number](/types/number) that specifies the analog period in microseconds. + +## Example The following code first sets `P0` to analog with **analog write pin**, and then sets the PWM period of `P0` to 20,000 microseconds. diff --git a/docs/reference/pins/analog-set-pitch-pin.md b/docs/reference/pins/analog-set-pitch-pin.md index 8bc6cd4fcc7..356421430aa 100644 --- a/docs/reference/pins/analog-set-pitch-pin.md +++ b/docs/reference/pins/analog-set-pitch-pin.md @@ -12,6 +12,8 @@ pins.analogSetPitchPin(AnalogPin.P0) ## Example +Set the pitch pin to `P0` and send a 440 Hz tone for 1 second. + ```blocks pins.analogSetPitchPin(AnalogPin.P0) let frequency = 440 @@ -26,5 +28,8 @@ pins.analogPitch(frequency, duration) ## See also -[@boardname@ pins](/device/pins), [analog set period](/reference/pins/analog-set-period), [analog pitch](/reference/pins/analog-pitch) +[@boardname@ pins](/device/pins), +[analog set period](/reference/pins/analog-set-period), +[analog pitch](/reference/pins/analog-pitch), +[set audio pin](/reference/pins/set-audio-pin) diff --git a/docs/reference/pins/digital-pin.md b/docs/reference/pins/digital-pin.md new file mode 100644 index 00000000000..f58c57e0031 --- /dev/null +++ b/docs/reference/pins/digital-pin.md @@ -0,0 +1,43 @@ +# digital Pin + +Get an digital pin number for a pin identifier. + +```sig +pins._digitalPin(DigitalPin.P3) +``` + +## Parameters + +* **pin**: a pin identifier for an digital pin (`P0` through `P20`). + +## Returns + +* a pin [number](/types/number) for the pin identifier. + +## Example: football score keeper + +This program reads pin `P0` to find when a goal is scored. When `P0` +is `1`, the program makes the score bigger and plays a buzzer sound +through `P2` with ``||pins:digital write pin||``. Use pin variables +to set the read and write pin numbers. + +```blocks +let score = 0 +let readPin = DigitalPin.P0 +let writePin = DigitalPin.P2 +basic.showNumber(score) +basic.forever(() => { + if (pins.digitalReadPin(readPin) == 1) { + score++; + pins.digitalWritePin(writePin, 1) + basic.showNumber(score) + basic.pause(1000) + pins.digitalWritePin(writePin, 0) + } +}) +``` + +## See also + +[digital read pin](/reference/pins/digital-read-pin), +[digital write pin](/reference/pins/digital-write-pin) diff --git a/docs/reference/pins/i2c-read-buffer.md b/docs/reference/pins/i2c-read-buffer.md index ccbddfcb082..d061b2ab375 100644 --- a/docs/reference/pins/i2c-read-buffer.md +++ b/docs/reference/pins/i2c-read-buffer.md @@ -10,7 +10,9 @@ A device connected to the I2C pins on the @boardname@ at the address is selected ### ~ hint -**Simulator**: This function needs real hardware to work with. It's not supported in the simulator. +#### Simulator + +This function needs real hardware to work with. It's not supported in the simulator. ### ~ diff --git a/docs/reference/pins/i2c-read-number.md b/docs/reference/pins/i2c-read-number.md index d27e5d2b786..9211ec73813 100644 --- a/docs/reference/pins/i2c-read-number.md +++ b/docs/reference/pins/i2c-read-number.md @@ -8,7 +8,9 @@ pins.i2cReadNumber(0, NumberFormat.Int8LE, false); ### ~ hint -**Simulator**: This function needs real hardware to work with. It's not supported in the simulator. +#### Simulator + +This function needs real hardware to work with. It's not supported in the simulator. ### ~ diff --git a/docs/reference/pins/i2c-write-buffer.md b/docs/reference/pins/i2c-write-buffer.md index 58e66d7ed1e..1be771ca6e9 100644 --- a/docs/reference/pins/i2c-write-buffer.md +++ b/docs/reference/pins/i2c-write-buffer.md @@ -10,7 +10,9 @@ A device connected to the I2C pins on the @boardname@ at the address is selected ### ~ hint -**Simulator**: This function needs real hardware to work with. It's not supported in the simulator. +#### Simulator + +This function needs real hardware to work with. It's not supported in the simulator. ### ~ @@ -24,7 +26,7 @@ A device connected to the I2C pins on the @boardname@ at the address is selected #### Repeated start -A [repeated start condition](http://www.i2c-bus.org/repeated-start-condition/) is set to help make sure that when you want to write data miltiple times from the device at once, it can happen without interruption. A start conditon is sent (if **repeated** is `true`) each time a buffer is written without a matching stop condition. When the last buffer is written, the stop conditon can be sent by setting **repeated** to `false`. For single writes, don't use **repeated** or set it to `false`. +A [repeated start condition](http://www.i2c-bus.org/repeated-start-condition/) is set to help make sure that when you want to write data multiple times from the device at once, it can happen without interruption. A start conditon is sent (if **repeated** is `true`) each time a buffer is written without a matching stop condition. When the last buffer is written, the stop conditon can be sent by setting **repeated** to `false`. For single writes, don't use **repeated** or set it to `false`. #### Reserved addresses diff --git a/docs/reference/pins/i2c-write-number.md b/docs/reference/pins/i2c-write-number.md index b630d9ee74c..879ad59f91a 100644 --- a/docs/reference/pins/i2c-write-number.md +++ b/docs/reference/pins/i2c-write-number.md @@ -8,7 +8,9 @@ pins.i2cWriteNumber(0, 0, NumberFormat.Int8LE, true); ### ~ hint -**Simulator**: This function needs real hardware to work with. It's not supported in the simulator. +#### Simulator + +This function needs real hardware to work with. It's not supported in the simulator. ### ~ diff --git a/docs/reference/pins/on-pulsed.md b/docs/reference/pins/on-pulsed.md index 4ec62f90b63..ed652f917e3 100644 --- a/docs/reference/pins/on-pulsed.md +++ b/docs/reference/pins/on-pulsed.md @@ -8,7 +8,9 @@ pins.onPulsed(DigitalPin.P0, PulseValue.High, () => { }); ### ~ hint -**Simulator**: This function needs real hardware to work with. It's not supported in the simulator. +#### Simulator + +This function needs real hardware to work with. It's not supported in the simulator. ### ~ diff --git a/docs/reference/pins/pulse-duration.md b/docs/reference/pins/pulse-duration.md index a03e64cdff7..d9d3e2f0986 100644 --- a/docs/reference/pins/pulse-duration.md +++ b/docs/reference/pins/pulse-duration.md @@ -10,7 +10,9 @@ A pin pulse is detected in the [onPulsed](/reference/pins/on-pulsed) event. You ### ~ hint -**Simulator**: This function needs real hardware to work with. It's not supported in the simulator. +#### Simulator + +This function needs real hardware to work with. It's not supported in the simulator. ### ~ diff --git a/docs/reference/pins/pulse-in.md b/docs/reference/pins/pulse-in.md index 4b580870e32..00b5c751606 100644 --- a/docs/reference/pins/pulse-in.md +++ b/docs/reference/pins/pulse-in.md @@ -15,7 +15,9 @@ Please read the [page about pins](/device/pins) carefully. ### ~ hint -**Simulator**: This function needs real hardware to work with. It's not supported in the simulator. +#### Simulator + +This function needs real hardware to work with. It's not supported in the simulator. ### ~ diff --git a/docs/reference/pins/set-audio-pin-enabled.md b/docs/reference/pins/set-audio-pin-enabled.md new file mode 100644 index 00000000000..ac04c187d53 --- /dev/null +++ b/docs/reference/pins/set-audio-pin-enabled.md @@ -0,0 +1,38 @@ +# set Audio Pin Enabled + +Enable a pin on the edge connector to output audio. + +```sig +pins.setAudioPinEnabled(false) +``` + +You can enable the @boardname@ to output audio to a pin on the edge connector. + +### ~ hint + +#### micro:bit V2 speaker + +With the [micro:bit V2](/device/v2) hardware, the built-in speaker will play (mirror) the same tones and music sent to the audio pin. + +### ~ + +## Parameters + +* **enabled**: audio is output to a pin is enabled if `true`, disabled if `false`. + +## Example + +Enable audio output to a pin on the edge connector and play a tone for the "A4" note at pin **P0** for 1 second. + +```blocks +pins.setAudioPinEnabled(false) +pins.setAudioPin(AnalogPin.P0) +let frequency = 440 +let duration = 1000 +pins.analogPitch(frequency, duration) +``` + +## See also + +[@boardname@ pins](/device/pins), [set audio pin](/reference/pins/set-audio-pin), +[analog set pitch pin](/reference/pins/analog-set-pitch-pin) diff --git a/docs/reference/pins/set-audio-pin.md b/docs/reference/pins/set-audio-pin.md index 458f0bac23c..aeab54fe4c6 100644 --- a/docs/reference/pins/set-audio-pin.md +++ b/docs/reference/pins/set-audio-pin.md @@ -31,4 +31,5 @@ pins.analogPitch(frequency, duration) ## See also -[@boardname@ pins](/device/pins), [analog set pitch pin](/reference/pins/analog-set-pitch-pin) +[@boardname@ pins](/device/pins), [set audio pin enabled](/reference/pins/set-audio-pin-enabled), +[analog set pitch pin](/reference/pins/analog-set-pitch-pin) diff --git a/docs/reference/pins/set-events.md b/docs/reference/pins/set-events.md index 3eac8aba89c..5396f640102 100644 --- a/docs/reference/pins/set-events.md +++ b/docs/reference/pins/set-events.md @@ -8,7 +8,9 @@ pins.setEvents(DigitalPin.P0, PinEventType.Edge); ### ~ hint -**Simulator**: This function needs real hardware to work with. It's not supported in the simulator. +#### Simulator + +This function needs real hardware to work with. It's not supported in the simulator. ### ~ diff --git a/docs/reference/pins/spi-format.md b/docs/reference/pins/spi-format.md index 7272b5c3ca9..a67d3e6d93e 100644 --- a/docs/reference/pins/spi-format.md +++ b/docs/reference/pins/spi-format.md @@ -10,7 +10,9 @@ The data sent over a SPI connection has a number of _bits_ to represent each val ### ~ hint -**Simulator**: This function needs real hardware to work with. It's not supported in the simulator. +#### Simulator + +This function needs real hardware to work with. It's not supported in the simulator. ### ~ diff --git a/docs/reference/pins/spi-frequency.md b/docs/reference/pins/spi-frequency.md index 7dd812415ee..2ebce5efe72 100644 --- a/docs/reference/pins/spi-frequency.md +++ b/docs/reference/pins/spi-frequency.md @@ -10,7 +10,9 @@ The @boardname@ sets the rate of data transfer and control timing for a SPI conn ### ~ hint -**Simulator**: This function needs real hardware to work with. It's not supported in the simulator. +#### Simulator + +This function needs real hardware to work with. It's not supported in the simulator. ### ~ diff --git a/docs/reference/pins/spi-pins.md b/docs/reference/pins/spi-pins.md index 3d75d1802f3..3887d09eeec 100644 --- a/docs/reference/pins/spi-pins.md +++ b/docs/reference/pins/spi-pins.md @@ -10,7 +10,9 @@ To configure the @boardname@ to write to an external device using a SPI connecti ### ~ hint -**Simulator**: This function needs real hardware to work with. It's not supported in the simulator. +#### Simulator + +This function needs real hardware to work with. It's not supported in the simulator. ### ~ diff --git a/docs/reference/pins/spi-write.md b/docs/reference/pins/spi-write.md index b7345f51cc1..70fdc950ed8 100644 --- a/docs/reference/pins/spi-write.md +++ b/docs/reference/pins/spi-write.md @@ -10,7 +10,9 @@ Data values are written to a SPI slave device connected to the @boardname@ by th ### ~ hint -**Simulator**: This function needs real hardware to work with. It's not supported in the simulator. +#### Simulator + +This function needs real hardware to work with. It's not supported in the simulator. ### ~ diff --git a/docs/reference/radio/on-data-received.md b/docs/reference/radio/on-data-received.md index ad18a561718..e1584e5549b 100644 --- a/docs/reference/radio/on-data-received.md +++ b/docs/reference/radio/on-data-received.md @@ -7,13 +7,13 @@ Run part of a program when the @boardname@ receives a radio.onDataReceived(() => { }); ``` -## ~ hint +### ~ alert -**Deprecated** +#### Deprecated API This API has been deprecated! Use [on received number](/reference/radio/on-received-number) instead. -## ~ +### ~ ```sig radio.onDataReceived(() => { }); diff --git a/docs/reference/radio/on-received-buffer.md b/docs/reference/radio/on-received-buffer.md index bd07af479fe..fc14bf14765 100644 --- a/docs/reference/radio/on-received-buffer.md +++ b/docs/reference/radio/on-received-buffer.md @@ -19,6 +19,7 @@ Two @boardname@s work like remote levels. They lie flat and detect any change in ```typescript let ax = 0; let ay = 0; +radio.setGroup(3) basic.forever(() => { ax = input.acceleration(Dimension.X); ay = input.acceleration(Dimension.Y); @@ -45,11 +46,11 @@ radio.onReceivedBuffer(function (receivedBuffer) { ``` -## ~hint +### ~hint A radio that can both transmit and receive is called a _transceiver_. -## ~ +### ~ ## See also diff --git a/docs/reference/radio/on-received-message.md b/docs/reference/radio/on-received-message.md new file mode 100644 index 00000000000..8b8188b2b37 --- /dev/null +++ b/docs/reference/radio/on-received-message.md @@ -0,0 +1,26 @@ +# @extends + +## Example #example + +Send a ``Hello`` message when button ``A`` is pressed, ``Goodbye`` when button ``B`` is pressed. If the messages are received, display either a ``heart`` for the ``Hello`` message or a ``scissor`` for the ``Goodbye`` message. + +```blocks +enum RadioMessage { + message1 = 49434, + Hello = 49337, + Goodbye = 16885 +} +radio.setGroup(3) +input.onButtonPressed(Button.A, function () { + radio.sendMessage(RadioMessage.Hello) +}) +radio.onReceivedMessage(RadioMessage.Hello, function () { + basic.showIcon(IconNames.Heart) +}) +input.onButtonPressed(Button.B, function () { + radio.sendMessage(RadioMessage.Goodbye) +}) +radio.onReceivedMessage(RadioMessage.Goodbye, function () { + basic.showIcon(IconNames.Scissors) +}) +``` \ No newline at end of file diff --git a/docs/reference/radio/on-received-number.md b/docs/reference/radio/on-received-number.md index ffa2e4e44bc..cc9086359b2 100644 --- a/docs/reference/radio/on-received-number.md +++ b/docs/reference/radio/on-received-number.md @@ -11,13 +11,15 @@ radio.onReceivedNumber(function (receivedNumber) {}) * **receivedNumber**: The [number](/types/number) that was sent in this packet or `0` if this packet did not contain a number. See [send number](/reference/radio/send-number) and [send value](/reference/radio/send-value) -## ~ hint +### ~ hint + +#### @boardname@ radio Watch this video to see how the radio hardware works on the @boardname@: https://www.youtube.com/watch?v=Re3H2ISfQE8 -## ~ +### ~ ## Examples @@ -29,6 +31,7 @@ thing from nearby @boardname@s. It shows these numbers as a [bar graph](/reference/led/plot-bar-graph). ```blocks +radio.setGroup(1) basic.forever(() => { radio.sendNumber(input.acceleration(Dimension.X)); }) @@ -43,6 +46,7 @@ This program uses the signal strength from received packets to graph the approximate distance between two @boardname@s. ```blocks +radio.setGroup(1) basic.forever(() => { radio.sendNumber(0) }) diff --git a/docs/reference/radio/on-received-string.md b/docs/reference/radio/on-received-string.md index cb22faf6f72..ab7637dae71 100644 --- a/docs/reference/radio/on-received-string.md +++ b/docs/reference/radio/on-received-string.md @@ -10,19 +10,22 @@ radio.onReceivedString(function (receivedString) {}) * **receivedString**: The [string](/types/string) that was sent in this packet or the empty string if this packet did not contain a string. See [send string](/reference/radio/send-string) and [send value](/reference/radio/send-value) -## ~ hint +### ~ hint + +#### @boardname@ radio Watch this video to see how the radio hardware works on the @boardname@: https://www.youtube.com/watch?v=Re3H2ISfQE8 -## ~ +### ~ ## Example This program continuously sends a cheerful message. It also receives a messages from nearby @boardname@s. It shows these messages on the screen. ```blocks +radio.setGroup(1) basic.forever(() => { radio.sendString("I'm happy"); }) diff --git a/docs/reference/radio/on-received-value.md b/docs/reference/radio/on-received-value.md index 8b61fb40d79..784dc285652 100644 --- a/docs/reference/radio/on-received-value.md +++ b/docs/reference/radio/on-received-value.md @@ -11,13 +11,15 @@ radio.onReceivedValue(function (name, value) {}) * **name**: a [string](/types/string) that is a name for the value received. * **value**: a [number](/types/number) that is the value received. -## ~ hint +### ~ hint + +#### @boardname@ radio Watch this video to see how the radio hardware works on the @boardname@: https://www.youtube.com/watch?v=Re3H2ISfQE8 -## ~ +### ~ ## Example @@ -27,6 +29,7 @@ thing from nearby @boardname@s, show the numbers as a [bar graph](/reference/led/plot-bar-graph). ```blocks +radio.setGroup(1) basic.forever(() => { radio.sendValue("accel-x", input.acceleration(Dimension.X)) }) diff --git a/docs/reference/radio/receive-number.md b/docs/reference/radio/receive-number.md index 83e00a40ae3..d5679fe8713 100644 --- a/docs/reference/radio/receive-number.md +++ b/docs/reference/radio/receive-number.md @@ -6,13 +6,13 @@ Receive the next number sent by a @boardname@ in the same ``radio`` group. radio.receiveNumber(); ``` -## ~ hint +### ~ alert -**Deprecated** +#### Deprecated API This API has been deprecated! Use [on received number](/reference/radio/on-received-number) instead. -## ~ +### ~ ## Returns diff --git a/docs/reference/radio/receive-string.md b/docs/reference/radio/receive-string.md index 3c6ae8143a9..b6c8545b179 100644 --- a/docs/reference/radio/receive-string.md +++ b/docs/reference/radio/receive-string.md @@ -5,13 +5,13 @@ Find the next string sent by radio from another @boardname@. ```sig radio.receiveString() ``` -## ~ hint +### ~ alert -**Deprecated** +#### Deprecated This API has been deprecated! Use [on received string](/reference/radio/on-received-string) instead. -## ~ +### ~ ## Returns diff --git a/docs/reference/radio/received-packet.md b/docs/reference/radio/received-packet.md index 3dd95504b54..389576eeb7e 100644 --- a/docs/reference/radio/received-packet.md +++ b/docs/reference/radio/received-packet.md @@ -18,16 +18,25 @@ In addition to a [number](types/number), [string](/types/string), or name-value ## Returns * a [number](/types/number) that is the property selected in the **type** parameter: ->* ``signal strength``: the value ranges from `-128` to `-42` (`-128` means a weak signal and `-42` means a strong one.) +>* ``signal strength``: the value ranges from `-128` up to `-28` (`-128` means a weak signal and `-28` means a strong one.) >* ``serial number``: the value is the serial number of the board sending the packet. >* ``time``: the value is the system time, in microseconds, of the sender at the time when the packet was sent. +### ~ hint + +#### Signal strength and board version + +Measurement of the received signal strength is dependent on what version of @boardname@ you have. The @boardname@ boards prior to v2 can typically measure a signal strength up to `-42` dBm. Now, v2 boards will measure a signal strength up to `-28` dBm (typical). + +### ~ + ## Example This program uses the signal strength from received packets to graph the approximate distance between two @boardname@s. ```blocks +radio.setGroup(1) basic.forever(() => { radio.sendNumber(0) }) diff --git a/docs/reference/radio/received-signal-strength.md b/docs/reference/radio/received-signal-strength.md index 955160409ed..de96300f615 100644 --- a/docs/reference/radio/received-signal-strength.md +++ b/docs/reference/radio/received-signal-strength.md @@ -6,29 +6,35 @@ Find how strong the radio signal is. radio.receivedSignalStrength(); ``` -## ~ hint +### ~ hint -**Deprecated** +#### Deprecated This API has been deprecated! Use [received packet](/reference/radio/received-packet) instead. -## ~ +### ~ -Find how strong the ``radio`` signal is, from `-128` to `-42`. -(`-128` means a weak signal and `-42` means a strong one.) +Find how strong the ``radio`` signal is, from `-128` to `-28`. +(`-128` means a weak signal and `-28` means a strong one.) The @boardname@ finds the signal strength by checking how strong it was the last time it ran the [on received number](/reference/radio/on-received-number) function. That means it needs to run **receive number** first. - - ## Returns -* a [number](/types/number) between `-128` and `-42` that means +* a [number](/types/number) between `-128` and `-28` that means how strong the signal is. +### ~ hint + +#### Signal strength and board version + +Measurement of the received signal strength is dependent on what version of @boardname@ you have. The @boardname@ boards prior to v2 can typically measure a signal strength up to `-42` dBm. Now, v2 boards will measure a signal strength up to `-28` dBm (typical). + +### ~ + ## Simulator This function only works on the @boardname@, not in browsers. @@ -49,7 +55,8 @@ basic.forever(() => { ## See also -[on received number](/reference/radio/on-received-number), [send number](/reference/radio/send-number), [on data received](/reference/radio/on-data-received) +[on received number](/reference/radio/on-received-number), [send number](/reference/radio/send-number), +[on data received](/reference/radio/on-data-received), [received packet](/reference/received-packet) ```package radio diff --git a/docs/reference/radio/send-buffer.md b/docs/reference/radio/send-buffer.md index 5d4975de199..2d9c27dc989 100644 --- a/docs/reference/radio/send-buffer.md +++ b/docs/reference/radio/send-buffer.md @@ -19,6 +19,7 @@ If you load this program onto two @boardname@s, each board will send the level i ```typescript let ax = 0; let ay = 0; +radio.setGroup(6) basic.forever(() => { ax = input.acceleration(Dimension.X); ay = input.acceleration(Dimension.Y); diff --git a/docs/reference/radio/send-message.md b/docs/reference/radio/send-message.md new file mode 100644 index 00000000000..8b8188b2b37 --- /dev/null +++ b/docs/reference/radio/send-message.md @@ -0,0 +1,26 @@ +# @extends + +## Example #example + +Send a ``Hello`` message when button ``A`` is pressed, ``Goodbye`` when button ``B`` is pressed. If the messages are received, display either a ``heart`` for the ``Hello`` message or a ``scissor`` for the ``Goodbye`` message. + +```blocks +enum RadioMessage { + message1 = 49434, + Hello = 49337, + Goodbye = 16885 +} +radio.setGroup(3) +input.onButtonPressed(Button.A, function () { + radio.sendMessage(RadioMessage.Hello) +}) +radio.onReceivedMessage(RadioMessage.Hello, function () { + basic.showIcon(IconNames.Heart) +}) +input.onButtonPressed(Button.B, function () { + radio.sendMessage(RadioMessage.Goodbye) +}) +radio.onReceivedMessage(RadioMessage.Goodbye, function () { + basic.showIcon(IconNames.Scissors) +}) +``` \ No newline at end of file diff --git a/docs/reference/radio/send-string.md b/docs/reference/radio/send-string.md index 2be216622de..ad08e9cfb4a 100644 --- a/docs/reference/radio/send-string.md +++ b/docs/reference/radio/send-string.md @@ -26,6 +26,7 @@ code word from one of them to the others by pressing button `A`. The other @boardname@s will receive the code word and then show it. ```blocks +radio.setGroup(1) input.onButtonPressed(Button.A, () => { radio.sendString("Codeword: TRIMARAN") basic.showString("SENT"); diff --git a/docs/reference/radio/set-group.md b/docs/reference/radio/set-group.md index 6c64044110c..01ecdee4842 100644 --- a/docs/reference/radio/set-group.md +++ b/docs/reference/radio/set-group.md @@ -1,35 +1,50 @@ # set Group -Make a program have the group ID you tell it for sending and receiving -with radio. +Set the group ID for sending and receiving messages over radio. ```sig -radio.setGroup(0); +radio.setGroup(0) ``` A group is like a cable channel (a @boardname@ can only -send or receive in one group at a time). A group ID is like the cable -channel number. +send or receive date in one group at a time) and the group ID is like the channel number. -If you do not tell your program which group ID to use with this -function, it will figure out its own group ID by itself. If you load -the very same program onto two different @boardname@s, they will be able +If you load the same program onto two different @boardname@s, they will be able to talk to each other because they will have the same group ID. ## Parameters -* **id**: a [number](/types/number) from ``0`` to ``255``. +* **id**: a radio group ID [number](/types/number) from ``0`` to ``255``. -## Simulator +### ~ reminder + +#### Default radio group + +If you haven't set a radio group for the @boardname@, it will use the default group number of **0**. + +### ~ + +### ~ alert + +#### Simulator This function only works on the @boardname@, not in browsers. +### ~ + ## Example -This program makes the group ID equal 128. +Set a radio group to send and receive a [number](/types/number) between @boardname@s. ```blocks -radio.setGroup(128) +radio.setGroup(1) +radio.onReceivedNumber(function (receivedNumber) { + basic.showNumber(0) + basic.clearScreen() +}) +input.onButtonPressed(Button.A, function () { + radio.sendNumber(0) +}) ``` ## See also @@ -41,6 +56,7 @@ radio.setGroup(128) [send value](/reference/radio/send-value), [send string](/reference/radio/send-string) + ```package radio ``` \ No newline at end of file diff --git a/docs/reference/radio/write-received-packet-to-serial.md b/docs/reference/radio/write-received-packet-to-serial.md index 843f323f009..27dbfd75a78 100644 --- a/docs/reference/radio/write-received-packet-to-serial.md +++ b/docs/reference/radio/write-received-packet-to-serial.md @@ -40,6 +40,7 @@ the second @boardname@), this program sends temperature data to the serial port. ```blocks +radio.setGroup(44) input.onButtonPressed(Button.A, function () { radio.sendNumber(input.temperature()) radio.sendValue("temp", input.temperature()) diff --git a/docs/reference/radio/write-value-to-serial.md b/docs/reference/radio/write-value-to-serial.md index d3feb48e2f3..d9cd6187818 100644 --- a/docs/reference/radio/write-value-to-serial.md +++ b/docs/reference/radio/write-value-to-serial.md @@ -29,6 +29,7 @@ the second @boardname@), this program sends temperature data to serial. ```blocks +radio.setGroup(1) input.onButtonPressed(Button.A, () => { radio.sendNumber(input.temperature()); }); diff --git a/docs/reference/serial.md b/docs/reference/serial.md index a59ed0ac610..2e66469e37f 100644 --- a/docs/reference/serial.md +++ b/docs/reference/serial.md @@ -19,6 +19,8 @@ serial.onDataReceived(",", () => {}) ```cards serial.redirect(SerialPin.P0, SerialPin.P0, BaudRate.BaudRate115200); serial.redirectToUSB(); +serial.setBaudRate(BaudRate.BaudRate115200) +serial.setWriteLinePadding(0) serial.writeBuffer(serial.readBuffer(64)); serial.readBuffer(64); serial.setRxBufferSize(64); @@ -31,7 +33,8 @@ serial.setTxBufferSize(64); [writeString](/reference/serial/write-string), [writeNumbers](/reference/serial/write-numbers), [readUntil](/reference/serial/read-until), [readLine](/reference/serial/read-line), [readString](/reference/serial/read-string), [onDataReceived](/reference/serial/on-data-received), -[redirect](/reference/serial/redirect), [writeBuffer](/reference/serial/write-buffer), [readBuffer](/reference/serial/read-buffer), +[redirect](/reference/serial/redirect), [set baud rate](/reference/serial/set-baud-rate), [set write line padding](/reference/serial/set-write-line-padding), +[writeBuffer](/reference/serial/write-buffer), [readBuffer](/reference/serial/read-buffer), [redirectToUSB](/reference/serial/redirect-to-usb), [set rx buffer size](/reference/serial/set-rx-buffer-size), [set tx buffer size](/reference/serial/set-tx-buffer-size) \ No newline at end of file diff --git a/docs/reference/serial/on-data-received.md b/docs/reference/serial/on-data-received.md index d8477bd45f9..82aeda7d7ba 100644 --- a/docs/reference/serial/on-data-received.md +++ b/docs/reference/serial/on-data-received.md @@ -1,10 +1,9 @@ -# Serial On Data Received +# on Data Received Registers an event to be fired when one of the delimiter is matched. - ```sig -serial.onDataReceived(",", () => {}) +serial.onDataReceived(",", function() {}) ``` ## Parameters @@ -13,10 +12,10 @@ serial.onDataReceived(",", () => {}) ## Example -Read values separated by `,`: +Read values separated by a comma `,`. ```blocks -serial.onDataReceived(serial.delimiters(Delimiters.Comma), () => { +serial.onDataReceived(serial.delimiters(Delimiters.Comma), function() { basic.showString(serial.readUntil(serial.delimiters(Delimiters.Comma))) }) ``` @@ -24,6 +23,5 @@ serial.onDataReceived(serial.delimiters(Delimiters.Comma), () => { ## See also [serial](/device/serial), -[serial write line](/reference/serial/write-line), -[serial write value](/reference/serial/write-value) - +[write line](/reference/serial/write-line), +[write value](/reference/serial/write-value) \ No newline at end of file diff --git a/docs/reference/serial/read-buffer.md b/docs/reference/serial/read-buffer.md index a6a841f828b..ffd542350dd 100644 --- a/docs/reference/serial/read-buffer.md +++ b/docs/reference/serial/read-buffer.md @@ -3,7 +3,7 @@ Read available serial data into a buffer. ```sig -serial.readBuffer(64); +serial.readBuffer(64) ``` ## Parameters @@ -16,13 +16,15 @@ Use ``0`` to return the available buffered data. * a [buffer](/types/buffer) containing input from the serial port. The length of the buffer may be smaller than the requested length. The length is 0 if any error occurs. -## ~hint -**Pause for more data** +### ~ hint + +#### Pause for more data If the desired number of characters are available, **readBuffer** returns a buffer with the expected size. If not, the calling fiber (the part of your program calling the **readBuffer** function) sleeps until the desired number of characters are finally read into the buffer. To avoid waiting for data, set the length to ``0`` so that buffered data is returned immediately. -## ~ + +### ~ ## Example @@ -31,8 +33,8 @@ Read character data from the serial port one row at a time. Write the rows to an ```typescript serial.setRxBufferSize(10) for (let i = 0; i < 24; i++) { - let rowData = serial.readBuffer(10); - pins.i2cWriteBuffer(65, rowData, false); + let rowData = serial.readBuffer(10) + pins.i2cWriteBuffer(65, rowData, false) } ``` @@ -42,14 +44,13 @@ Read available data and process it as it comes. ```typescript basic.forever(function() { - let rowData = serial.readBuffer(0); + let rowData = serial.readBuffer(0) if (rowData.length > 0) { // do something!!! } }) ``` - ## See Also [write buffer](/reference/serial/write-buffer) diff --git a/docs/reference/serial/read-line.md b/docs/reference/serial/read-line.md index 0d621cfeac4..5c4a9ff8ff0 100644 --- a/docs/reference/serial/read-line.md +++ b/docs/reference/serial/read-line.md @@ -1,18 +1,19 @@ -# Serial Read Line +# read Line Read a line of text from the serial port. ```sig -serial.readLine(); +serial.readLine() ``` ### ~hint +#### Newline characters + This function expects the line it reads to be terminated with the `\n` -character. If your terminal software does not terminate lines with +character. If your terminal software does not terminate lines with `\n`, this function will probably never return a value. - You can override the ``serial.NEW_LINE_DELIMITER`` field to change the newline delimiter. ### ~ @@ -27,15 +28,15 @@ The following example requests the user's name, then repeats it to greet the use ```blocks basic.forever(() => { - serial.writeLine("What is your name?"); - let answer = serial.readLine(); - serial.writeString("Hello,"); - serial.writeLine(answer); -}); + serial.writeLine("What is your name?") + let answer = serial.readLine() + serial.writeString("Hello,") + serial.writeLine(answer) +}) ``` ## See also [serial](/device/serial), -[serial write line](/reference/serial/write-line), -[serial write value](/reference/serial/write-value) +[write line](/reference/serial/write-line), +[write value](/reference/serial/write-value) diff --git a/docs/reference/serial/read-string.md b/docs/reference/serial/read-string.md index 281d0eefb7c..eca5993f00a 100644 --- a/docs/reference/serial/read-string.md +++ b/docs/reference/serial/read-string.md @@ -1,9 +1,9 @@ -# Serial Read String +# read String Read the buffered serial data as a string. ```sig -serial.readString(); +serial.readString() ``` ## Returns @@ -15,13 +15,13 @@ serial.readString(); The following program scrolls text on the screen as it arrives from serial. ```blocks -basic.forever(() => { - basic.showString(serial.readString()); -}); +basic.forever(function() { + basic.showString(serial.readString()) +}) ``` ## See also [serial](/device/serial), -[serial write line](/reference/serial/write-line), -[serial write value](/reference/serial/write-value) +[write line](/reference/serial/write-line), +[write value](/reference/serial/write-value) diff --git a/docs/reference/serial/read-until.md b/docs/reference/serial/read-until.md index 484501f3647..e3621f0f670 100644 --- a/docs/reference/serial/read-until.md +++ b/docs/reference/serial/read-until.md @@ -1,9 +1,9 @@ -# Serial Read Until +# read Until Read a text from the serial port until a delimiter is found. ```sig -serial.readUntil(","); +serial.readUntil(",") ``` ## Returns @@ -16,13 +16,13 @@ The following example reads strings separated by commands (``,``). ```blocks basic.forever(() => { - let answer = serial.readUntil(","); - serial.writeLine(answer); -}); + let answer = serial.readUntil(",") + serial.writeLine(answer) +}) ``` ## See also [serial](/device/serial), -[serial write line](/reference/serial/write-line), -[serial write value](/reference/serial/write-value) +[write line](/reference/serial/write-line), +[write value](/reference/serial/write-value) diff --git a/docs/reference/serial/redirect.md b/docs/reference/serial/redirect.md index 4563c64687c..3c6f0630508 100644 --- a/docs/reference/serial/redirect.md +++ b/docs/reference/serial/redirect.md @@ -3,7 +3,7 @@ Configure the serial port to use the pins instead of USB. ```sig -serial.redirect(SerialPin.P0, SerialPin.P0, BaudRate.BaudRate115200); +serial.redirect(SerialPin.P0, SerialPin.P0, BaudRate.BaudRate115200) ``` The default connection for the serial port is over a USB cable. You can have the serial data go across wires connected to pins on the @boardname@ instead. To set the input and output for the serial connection to be on the pins, you redirect it to the pins. Also, you decide how fast you want to send and receive the data on the pins by choosing a _baud_ rate. @@ -14,8 +14,9 @@ The default connection for the serial port is over a USB cable. You can have the * **rate**: the baud rate for transmitting and receiving data. Baud rates you can choose from are: >`300`, `1200`, `2400`, `4800`, `9600`, `14400`, `19200,`, `28800`, `31250`, `38400`, `57600`, or `115200` -## ~hint -**Baud rate** +### ~hint + +#### Baud rate Serial communication transmits data by sending one bit of a [digital number](/types/buffer/number-format) (usually a byte sized number), at a time. So, the data bytes are sent as a series of their bits. Serial communication uses just one wire to send these bits so only one bit can travel across the wire at a time. @@ -23,7 +24,7 @@ When pins on your @boardname@ are configured for serial communication, they make You will typically use `9600` or `115200` for your baud rate. Sometimes the device you connect to can figure out what your baud rate is. Most of the time though, you need to make sure the device you connect to is set to match your baud rate. -## ~ +### ~ ## Example @@ -32,9 +33,9 @@ serial port to use the pins. The new configuration uses pin ``P1`` to transmit a ``P2`` to receive. The baud rate is set to `9600`. ```blocks -input.onButtonPressed(Button.A, () => { - serial.redirect(SerialPin.P1, SerialPin.P2, BaudRate.BaudRate9600); -}); +input.onButtonPressed(Button.A, function() { + serial.redirect(SerialPin.P1, SerialPin.P2, BaudRate.BaudRate9600) +}) ``` ## See also diff --git a/docs/reference/serial/set-baud-rate.md b/docs/reference/serial/set-baud-rate.md new file mode 100644 index 00000000000..0c5726696bd --- /dev/null +++ b/docs/reference/serial/set-baud-rate.md @@ -0,0 +1,56 @@ +# set Baud Rate + +Set the baud rate of the serial connection. + +```sig +serial.setBaudRate(BaudRate.BaudRate115200) +``` + +The baud rate of the serial connection is the speed at which it will transmit data. You can set one of several standard rates for the transmit speed. The receiving @boardname@ or device must be set to receive data at the same speed as the sending @boardname@. + +### ~ hint + +#### Bits and bauds + +Baud, or _baud rate_, is a very old measure of data speed. It originates from the early days of _teletype_ when characters of the alphabet were transmitted over telegraph wires. Signal changes on the wires are used to encode a sequence of bits that represented a character in a message. The baud rate is how many times per second these signal changes happen. When binary data (digital bits) is transmitted over an analog system, like telegraph or telephone wires, the bits are _modulated_ by +a signal changing scheme to represent them. Sometimes mutliple bits are transmitted in a signal +change which makes the actual _bit rate_ faster than the baud rate. + +### ~ + +## Parameters + +* **rate**: The baud rate to set for the serial connection. The default rate is `115200` baud, The rates to choose from are: +>* `1200` baud +>* `2400` baud +>* `4800` baud +>* `9600` baud +>* `14400` baud +>* `19200` baud +>* `28800` baud +>* `31250` baud +>* `38400` baud +>* `57600` baud +>* `115200` baud + +### ~reminder + +#### Logging serial data + +In order for the serial console log to record your serial data, the baud rate MUST remain +at `115200` (the default). + +### ~ + +## Example + +Set the baud rate to `9600` and send a message over USB serial to a computer. + +```blocks +serial.setBaudRate(BaudRate.BaudRate9600) +serial.writeString("This is my SERIAL message!") +``` + +## See also + +[redirect](/reference/serial/redirect) \ No newline at end of file diff --git a/docs/reference/serial/set-rx-buffer-size.md b/docs/reference/serial/set-rx-buffer-size.md index 1fc58aa4de4..58ca0e4c66e 100644 --- a/docs/reference/serial/set-rx-buffer-size.md +++ b/docs/reference/serial/set-rx-buffer-size.md @@ -8,7 +8,7 @@ serial.setRxBufferSize(10) ## Parameters -* **size**: desired length of the reception buffer +* **size**: desired length of the reception buffer (maximum 254) ## Example diff --git a/docs/reference/serial/set-tx-buffer-size.md b/docs/reference/serial/set-tx-buffer-size.md index 31b69b9a703..99e9f5f93b7 100644 --- a/docs/reference/serial/set-tx-buffer-size.md +++ b/docs/reference/serial/set-tx-buffer-size.md @@ -8,7 +8,7 @@ serial.setTxBufferSize(10) ## Parameters -* **size**: desired length of the transmission buffer +* **size**: desired length of the transmission buffer (maximum 254) ## Example diff --git a/docs/reference/serial/set-write-line-padding.md b/docs/reference/serial/set-write-line-padding.md new file mode 100644 index 00000000000..02e05063623 --- /dev/null +++ b/docs/reference/serial/set-write-line-padding.md @@ -0,0 +1,51 @@ +# set Write Line Padding + +Sets the padding length for text lines written to the serial port. + +```sig +serial.setWriteLinePadding(0) +``` + +When text is written to the serial port as a "line", it can have an amount of padding to keep the line at a certian length. If the write line padding is set to `32` and the length of text sent with [write line](/reference/serial/write-line) is only `15` characters, then additional `space` characters are added to make the line length `32` characters. + +Also, the padding length will account for the NEWLINE characters that terminate the line. + +### ~ hint + +#### Serial input buffers + +Some devices that you connect a @boardname@ to with the serial port might collect the text you send to them in a buffer before they transfer it to a program that will process it. You can ensure that the connected device will respond to your messege by using padding to make the text you sent transfer out of the connected device's input buffer right away. If you know that the device connected to your @boardname@ will release the text in its input buffer when `64` characters are collected, you can set the write line padding length to `64` before you send your message. + +### ~ + +In this example, the a line of text `"Hello Serial!"` is written to the serial port. + +```block +serial.setWriteLinePadding(24) +serial.writeLine("Hello Serial!") +``` + +In this case, the output will NOT be: + +`Hello Serial!\r\n` + +Instead, it will include addtional space characters to make the line length `24` characters: + +`Hello Serial! \r\n` + +## Parameters + +* **length**: a [number](/types/number) between `0` and `128` that sets the padding length for lines of text written to the serial port. The default padding length is `32`. + +## Example + +Set the write line padding to `48` characters and write a message line to the serial port. + +```block +serial.setWriteLinePadding(48) +serial.writeString("This is my SERIAL message!") +``` + +## See also + +[write line](/reference/serial/write-line) \ No newline at end of file diff --git a/docs/reference/serial/write-buffer.md b/docs/reference/serial/write-buffer.md index d9c3c6fbb55..bf34db5c9d0 100644 --- a/docs/reference/serial/write-buffer.md +++ b/docs/reference/serial/write-buffer.md @@ -3,7 +3,7 @@ Write a buffer to the [serial](/device/serial) port. ```sig -serial.writeBuffer(pins.createBuffer(0)); +serial.writeBuffer(pins.createBuffer(0)) ``` You place your data characters into an existing buffer. All of the data, the length of the buffer, is written to the serial port. @@ -17,9 +17,9 @@ You place your data characters into an existing buffer. All of the data, the len Read some characters of data from a device connected to the I2C pins. Write the data to the serial port. ```typescript -pins.i2cWriteNumber(132, NumberFormat.UInt8LE, 0); -let i2cBuffer = pins.i2cReadBuffer(132, 16, false); -serial.writeBuffer(i2cBuffer); +pins.i2cWriteNumber(132, NumberFormat.UInt8LE, 0) +let i2cBuffer = pins.i2cReadBuffer(132, 16, false) +serial.writeBuffer(i2cBuffer) ``` ## See also diff --git a/docs/reference/serial/write-line.md b/docs/reference/serial/write-line.md index 7e37741fea8..bec3991de5f 100644 --- a/docs/reference/serial/write-line.md +++ b/docs/reference/serial/write-line.md @@ -1,10 +1,10 @@ -# Serial Write Line +# write Line Write a string to the [serial](/device/serial) port and start a new line of text by writing `\r\n`. ```sig -serial.writeLine(""); +serial.writeLine("") ``` ## Parameters @@ -18,10 +18,10 @@ serial.writeLine(""); Write the word `BOFFO` to the serial port repeatedly. ```blocks -basic.forever(() => { - serial.writeLine("BOFFO"); - basic.pause(5000); -}); +basic.forever(function() { + serial.writeLine("BOFFO") + basic.pause(5000) +}) ``` ### Streaming data @@ -31,7 +31,7 @@ Check the [compass heading](/reference/input/compass-heading) and show the direc ```blocks let degrees = 0 let direction = "" -basic.forever(() => { +basic.forever(function() { degrees = input.compassHeading() if (degrees < 45) { basic.showArrow(ArrowNames.North) @@ -57,6 +57,6 @@ basic.forever(() => { ## See also [serial](/device/serial), -[serial write number](/reference/serial/write-number), -[serial write string](/reference/serial/write-string), -[serial write value](/reference/serial/write-value) +[write number](/reference/serial/write-number), +[write string](/reference/serial/write-string), +[write value](/reference/serial/write-value) diff --git a/docs/reference/serial/write-number.md b/docs/reference/serial/write-number.md index 8e73eacd642..922926cc694 100644 --- a/docs/reference/serial/write-number.md +++ b/docs/reference/serial/write-number.md @@ -1,42 +1,72 @@ -# Serial Write Number +# write Number Write a number to the [serial](/device/serial) port. ```sig -serial.writeNumber(0); +serial.writeNumber(0) +``` + +A number value is written to the serial port as characters in a string representation. The number `876`, for example: + +```block +serial.writeNumber(876) +``` + +The receiving serial port will see this number as the 3 characters: + +``` +876 ``` ## Parameters * `value` is the [number](/types/number) to write to the serial port -## Example: one two three +### ~ reminder + +#### Simulator data log + +When a number is written to the serial port, it's sent immediately over the serial connection. However, when you code with ``||serial:write number||`` in the Editor, the simulator's data log may not display the output data right away. The characters that represent the number may get queued in the data log buffer and won't display until: -This program repeatedly writes a 3-digit number to the serial port. +* a 'newline' line character is received (`\n`) + +-- or -- + +* log data buffer limit is reached (currently set at `255` characters). + +If you want to see the string displayed immediately, use a ``||serial:write line||`` with an empty string right after the ``||serial:write number||``. + +### ~ + +## Examples + +### One, Two, Three + +This program repeatedly writes a 3-digit number as a line to the serial port. ```blocks -basic.forever(() => { - serial.writeNumber(123); - basic.pause(5000); +basic.forever(function() { + serial.writeNumber(123) + serial.writeLine("") + basic.pause(5000) }); ``` -## Example: plot bar graph does serial +### Plot bar graph does serial If you use the ``led.plotBarGraph`` function, it writes the number being plotted to the serial port too. ```blocks -basic.forever(() => { +basic.forever(function() { led.plotBarGraph(input.lightLevel(), 255) - basic.pause(10000); + basic.pause(10000) }) ``` ## See also [serial](/device/serial), -[serial write line](/reference/serial/write-line), -[serial write value](/reference/serial/write-value), -[serial write numbers](/reference/serial/write-numbers) - +[write line](/reference/serial/write-line), +[write value](/reference/serial/write-value), +[write numbers](/reference/serial/write-numbers) \ No newline at end of file diff --git a/docs/reference/serial/write-numbers.md b/docs/reference/serial/write-numbers.md index dbe0eb56d9c..0c9fd800886 100644 --- a/docs/reference/serial/write-numbers.md +++ b/docs/reference/serial/write-numbers.md @@ -1,9 +1,9 @@ -# Serial Write Numbers +# write Numbers Write an array of numbers to the [serial](/device/serial) port. ```sig -serial.writeNumbers([0, 1, 2]); +serial.writeNumbers([0, 1, 2]) ``` Instead of writing a single number at a time using [write number](/reference/serial/write-number), you can write multiple numbers to the serial port at once. They are written as _Comma Separated Values (CSV)_. @@ -23,17 +23,17 @@ This makes a line of CSV data where the commas between the numbers are the separ This program repeatedly writes a 3-number array to the serial port. ```blocks -basic.forever(() => { - serial.writeNumbers([1, 2, 3]); - basic.pause(5000); -}); +basic.forever(function() { + serial.writeNumbers([1, 2, 3]) + basic.pause(5000) +}) ``` ## Example: plot temperature and light ```blocks serial.writeLine("temp,light") -basic.forever(() => { +basic.forever(function() { serial.writeNumbers([input.temperature(), input.lightLevel()]) }) ``` @@ -41,5 +41,5 @@ basic.forever(() => { ## See also [serial](/device/serial), -[serial write line](/reference/serial/write-line), -[serial write value](/reference/serial/write-value) +[write line](/reference/serial/write-line), +[write value](/reference/serial/write-value) diff --git a/docs/reference/serial/write-string.md b/docs/reference/serial/write-string.md index 0e9f4941a43..e1361820f58 100644 --- a/docs/reference/serial/write-string.md +++ b/docs/reference/serial/write-string.md @@ -1,12 +1,28 @@ -# Serial Write String +# write String Write a string to the [serial](/device/serial) port, without starting a new line afterward. ```sig -serial.writeString(""); +serial.writeString("") ``` +### ~ reminder + +#### Simulator data log + +When a string is written to the serial port, it's sent immediately over the serial connection. However, when you code with ``||serial:write string||`` in the Editor, the simulator's data log may not display the output data right away. The characters that represent the string may get queued in the data log buffer and won't display until: + +* a 'newline' line character is received (`\n`) + +-- or -- + +* log data buffer limit is reached (currently set at `255` characters). + +If you want to see the string displayed immediately, use a ``||serial:write line||`` with an empty string right after the ``||serial:write string||``. + +### ~ + ## Parameters * `text` is the [string](/types/string) to write to the serial port @@ -17,15 +33,15 @@ This program writes the word `JUMBO` to the serial port repeatedly, without any new lines. ```blocks -basic.forever(() => { - serial.writeString("JUMBO"); - basic.pause(1000); -}); +basic.forever(function() { + serial.writeString("JUMBO") + basic.pause(1000) +}) ``` ## See also [serial](/device/serial), -[serial write line](/reference/serial/write-line), -[serial write number](/reference/serial/write-number), -[serial write value](/reference/serial/write-value) +[write line](/reference/serial/write-line), +[write number](/reference/serial/write-number), +[write value](/reference/serial/write-value) diff --git a/docs/reference/serial/write-value.md b/docs/reference/serial/write-value.md index e10fb5460ce..7bb4fa0e295 100644 --- a/docs/reference/serial/write-value.md +++ b/docs/reference/serial/write-value.md @@ -3,7 +3,7 @@ Write a **name:value** pair and a newline character (`\r\n`) to the [serial](/device/serial) port. ```sig -serial.writeValue("x", 0); +serial.writeValue("x", 0) ``` It is common when reporting or recording data to use a _Name Value Pair_ (NVP). They appear as a text output string in the form of a _name_ and a _value_ together. The name and the value are separated in the string with a _colon_, `:`. A name value pair reporting a temperature of `-15` degrees could look like: @@ -31,17 +31,19 @@ Every 10 seconds, the example below sends the temperature and light level to the serial port. ```blocks -basic.forever(() => { +basic.forever(function() { serial.writeValue("temp", input.temperature()) serial.writeValue("light", input.lightLevel()) - basic.pause(10000); + basic.pause(10000) }) ``` ### ~hint +#### Radio-Serial gateway + The [send value](/reference/radio/send-value) function broadcasts -string/number pairs. You can use a second @boardname@ to receive them, +string/number pairs. You can use a second @boardname@ to receive them, and then send them directly to the serial port with ``write value``. ### ~ @@ -49,6 +51,6 @@ and then send them directly to the serial port with ``write value``. ## See also [serial](/device/serial), -[serial write line](/reference/serial/write-line), -[serial write number](/reference/serial/write-number), +[write line](/reference/serial/write-line), +[write number](/reference/serial/write-number), [send value](/reference/radio/send-value) diff --git a/docs/reference/text.md b/docs/reference/text.md index a7832884d92..20558ced4a8 100644 --- a/docs/reference/text.md +++ b/docs/reference/text.md @@ -1,18 +1 @@ -# Text - -Functions to combine, split, search, and convert text strings. - -```cards -"".charAt(0); -"".compare(""); -"".substr(0, 0); -parseFloat(""); -parseInt(""); -convertToText(0); -``` - -## See also - -[char at](/reference/text/char-at), [compare](/reference/text/compare), -[substr](/reference/text/substr), [parse int](/reference/text/parse-int), -[parse float](/reference/text/parse-float), [convert to text](/reference/text/convert-text) \ No newline at end of file +# @extends \ No newline at end of file diff --git a/docs/robots.txt b/docs/robots.txt new file mode 100644 index 00000000000..5f0db264575 --- /dev/null +++ b/docs/robots.txt @@ -0,0 +1,5 @@ +# robots.txt for PXT Micro:Bit + +# Disable crawling in /v2 path for micro:bit +User-agent: * +Disallow: /v2 diff --git a/docs/share.md b/docs/share.md new file mode 100644 index 00000000000..41e9afa5c59 --- /dev/null +++ b/docs/share.md @@ -0,0 +1,7 @@ +# @extends + +## Editing Shared Projects #editing + +By default, all shared projects in MakeCode can be copied and edited. There is no way to share a read-only project, or a project where people can’t make a copy or can’t see the code. When a user opens a project from a Share Link (either anonymous or persistent), they can see the simulator, the blocks, or the text code. They can now download the code onto the micro:bit. They may also make a copy of the project and edit it by pressing the Edit or Edit Code buttons in the top right corner. Any edits they make will not change the original project - if they want to share their changes back, they will have to create a new share link. + +![Edit shared project button](/static/share/edit-shared-project.png) diff --git a/docs/stable-ref.json b/docs/stable-ref.json index c6e6a05917c..cd0fd7d91b7 100644 --- a/docs/stable-ref.json +++ b/docs/stable-ref.json @@ -1,3 +1,3 @@ { - "appref": "v3.0" + "appref": "v9.0" } diff --git a/docs/static/Microsoft_logo_rgb_W-white_D-square.png b/docs/static/Microsoft_logo_rgb_W-white_D-square.png new file mode 100644 index 00000000000..106597ae039 Binary files /dev/null and b/docs/static/Microsoft_logo_rgb_W-white_D-square.png differ diff --git a/docs/static/Microsoft_logo_rgb_W-white_D.png b/docs/static/Microsoft_logo_rgb_W-white_D.png new file mode 100644 index 00000000000..d6139e3d0af Binary files /dev/null and b/docs/static/Microsoft_logo_rgb_W-white_D.png differ diff --git a/docs/static/blocks/block-menu.jpg b/docs/static/blocks/block-menu.jpg new file mode 100644 index 00000000000..7b29d6bf956 Binary files /dev/null and b/docs/static/blocks/block-menu.jpg differ diff --git a/docs/static/blocks/insert-comment.jpg b/docs/static/blocks/insert-comment.jpg new file mode 100644 index 00000000000..940f57e30da Binary files /dev/null and b/docs/static/blocks/insert-comment.jpg differ diff --git a/docs/static/blocks/variables/assign.gif b/docs/static/blocks/variables/assign.gif new file mode 100644 index 00000000000..1e6a56545ba Binary files /dev/null and b/docs/static/blocks/variables/assign.gif differ diff --git a/docs/static/blocks/variables/create.gif b/docs/static/blocks/variables/create.gif new file mode 100644 index 00000000000..d60ec394e56 Binary files /dev/null and b/docs/static/blocks/variables/create.gif differ diff --git a/docs/static/blocks/variables/string.gif b/docs/static/blocks/variables/string.gif new file mode 100644 index 00000000000..77e63dd20b6 Binary files /dev/null and b/docs/static/blocks/variables/string.gif differ diff --git a/docs/static/coding-for-teachers/playlist.png b/docs/static/coding-for-teachers/playlist.png new file mode 100644 index 00000000000..e0292018fb8 Binary files /dev/null and b/docs/static/coding-for-teachers/playlist.png differ diff --git a/docs/static/configurations/chrome-version.png b/docs/static/configurations/chrome-version.png index b7a4db67806..8195b7630ee 100644 Binary files a/docs/static/configurations/chrome-version.png and b/docs/static/configurations/chrome-version.png differ diff --git a/docs/static/configurations/edge-version.png b/docs/static/configurations/edge-version.png index f9c9095ad5b..3d8338cada4 100644 Binary files a/docs/static/configurations/edge-version.png and b/docs/static/configurations/edge-version.png differ diff --git a/docs/static/configurations/ie-version.png b/docs/static/configurations/ie-version.png index 3fbc6e7c36d..7d8421413bd 100644 Binary files a/docs/static/configurations/ie-version.png and b/docs/static/configurations/ie-version.png differ diff --git a/docs/static/configurations/osx-version.png b/docs/static/configurations/osx-version.png index 9457d7e48e3..71b580dbf25 100644 Binary files a/docs/static/configurations/osx-version.png and b/docs/static/configurations/osx-version.png differ diff --git a/docs/static/configurations/windows-version.png b/docs/static/configurations/windows-version.png index 06847ede75d..6fcdf51cff2 100644 Binary files a/docs/static/configurations/windows-version.png and b/docs/static/configurations/windows-version.png differ diff --git a/docs/static/courses/armu-micro-course.png b/docs/static/courses/armu-micro-course.png new file mode 100644 index 00000000000..7056be8e284 Binary files /dev/null and b/docs/static/courses/armu-micro-course.png differ diff --git a/docs/static/courses/codejoy.png b/docs/static/courses/codejoy.png new file mode 100644 index 00000000000..b13c985501b Binary files /dev/null and b/docs/static/courses/codejoy.png differ diff --git a/docs/static/courses/csintro/accelerometer/accelerometer.png b/docs/static/courses/csintro/accelerometer/accelerometer.png new file mode 100644 index 00000000000..0b3fc45b45c Binary files /dev/null and b/docs/static/courses/csintro/accelerometer/accelerometer.png differ diff --git a/docs/static/courses/csintro/accelerometer/axes.png b/docs/static/courses/csintro/accelerometer/axes.png new file mode 100644 index 00000000000..69e3d3e45a3 Binary files /dev/null and b/docs/static/courses/csintro/accelerometer/axes.png differ diff --git a/docs/static/courses/csintro/accelerometer/bit.png b/docs/static/courses/csintro/accelerometer/bit.png new file mode 100644 index 00000000000..9879ab593d4 Binary files /dev/null and b/docs/static/courses/csintro/accelerometer/bit.png differ diff --git a/docs/static/courses/csintro/accelerometer/global.png b/docs/static/courses/csintro/accelerometer/global.png new file mode 100644 index 00000000000..0281b159ce0 Binary files /dev/null and b/docs/static/courses/csintro/accelerometer/global.png differ diff --git a/docs/static/courses/csintro/accelerometer/highvelocitylowaccel.png b/docs/static/courses/csintro/accelerometer/highvelocitylowaccel.png new file mode 100644 index 00000000000..6d9a67b2f8e Binary files /dev/null and b/docs/static/courses/csintro/accelerometer/highvelocitylowaccel.png differ diff --git a/docs/static/courses/csintro/accelerometer/lowvelocityhighaccel.png b/docs/static/courses/csintro/accelerometer/lowvelocityhighaccel.png new file mode 100644 index 00000000000..d70fe53bbf7 Binary files /dev/null and b/docs/static/courses/csintro/accelerometer/lowvelocityhighaccel.png differ diff --git a/docs/static/courses/csintro/accelerometer/shake.jpg b/docs/static/courses/csintro/accelerometer/shake.jpg new file mode 100644 index 00000000000..ab0f3522963 Binary files /dev/null and b/docs/static/courses/csintro/accelerometer/shake.jpg differ diff --git a/docs/static/courses/csintro/accelerometer/velocity.png b/docs/static/courses/csintro/accelerometer/velocity.png new file mode 100644 index 00000000000..6aa4cdb2e00 Binary files /dev/null and b/docs/static/courses/csintro/accelerometer/velocity.png differ diff --git a/docs/static/courses/csintro/algorithms/add-comment.png b/docs/static/courses/csintro/algorithms/add-comment.png index b981bc310ee..9a5af8f3771 100644 Binary files a/docs/static/courses/csintro/algorithms/add-comment.png and b/docs/static/courses/csintro/algorithms/add-comment.png differ diff --git a/docs/static/courses/csintro/algorithms/write-comment.png b/docs/static/courses/csintro/algorithms/write-comment.png index a21b5dee38d..b25d7a18ca2 100644 Binary files a/docs/static/courses/csintro/algorithms/write-comment.png and b/docs/static/courses/csintro/algorithms/write-comment.png differ diff --git a/docs/static/courses/csintro/making/micropet-fox.jpg b/docs/static/courses/csintro/making/micropet-fox.jpg index aaf7591b3b5..0476c4832cf 100644 Binary files a/docs/static/courses/csintro/making/micropet-fox.jpg and b/docs/static/courses/csintro/making/micropet-fox.jpg differ diff --git a/docs/static/courses/csintro/making/micropet-piggy-bank.jpg b/docs/static/courses/csintro/making/micropet-piggy-bank.jpg index fe1248fd32f..b127e447a33 100644 Binary files a/docs/static/courses/csintro/making/micropet-piggy-bank.jpg and b/docs/static/courses/csintro/making/micropet-piggy-bank.jpg differ diff --git a/docs/static/courses/csintro/making/micropet-robot.jpg b/docs/static/courses/csintro/making/micropet-robot.jpg index 37ca5b10e20..6739a9f0d7a 100644 Binary files a/docs/static/courses/csintro/making/micropet-robot.jpg and b/docs/static/courses/csintro/making/micropet-robot.jpg differ diff --git a/docs/static/courses/first-lessons.png b/docs/static/courses/first-lessons.png new file mode 100644 index 00000000000..34b80c0722e Binary files /dev/null and b/docs/static/courses/first-lessons.png differ diff --git a/docs/static/courses/maker-ed-cyber-arcade.png b/docs/static/courses/maker-ed-cyber-arcade.png new file mode 100644 index 00000000000..252d2c401f3 Binary files /dev/null and b/docs/static/courses/maker-ed-cyber-arcade.png differ diff --git a/docs/static/courses/mr-morrison/beyond-basics.png b/docs/static/courses/mr-morrison/beyond-basics.png new file mode 100644 index 00000000000..3f581ad3800 Binary files /dev/null and b/docs/static/courses/mr-morrison/beyond-basics.png differ diff --git a/docs/static/courses/mr-morrison/data-sustainability.png b/docs/static/courses/mr-morrison/data-sustainability.png new file mode 100644 index 00000000000..5de39fceee9 Binary files /dev/null and b/docs/static/courses/mr-morrison/data-sustainability.png differ diff --git a/docs/static/courses/mr-morrison/starter-lessons.png b/docs/static/courses/mr-morrison/starter-lessons.png new file mode 100644 index 00000000000..917d0e25541 Binary files /dev/null and b/docs/static/courses/mr-morrison/starter-lessons.png differ diff --git a/docs/static/courses/ucp-science/egg-drop/newton-1st-law.png b/docs/static/courses/ucp-science/egg-drop/newton-1st-law.png new file mode 100644 index 00000000000..d7245ddca36 Binary files /dev/null and b/docs/static/courses/ucp-science/egg-drop/newton-1st-law.png differ diff --git a/docs/static/courses/ucp-science/egg-drop/newton-2nd-law.png b/docs/static/courses/ucp-science/egg-drop/newton-2nd-law.png new file mode 100644 index 00000000000..8e5a507f20c Binary files /dev/null and b/docs/static/courses/ucp-science/egg-drop/newton-2nd-law.png differ diff --git a/docs/static/courses/ucp-science/egg-drop/newton-3rd-law.png b/docs/static/courses/ucp-science/egg-drop/newton-3rd-law.png new file mode 100644 index 00000000000..150cfd4d016 Binary files /dev/null and b/docs/static/courses/ucp-science/egg-drop/newton-3rd-law.png differ diff --git a/docs/static/courses/ucp-science/spoon-race/egg-and-spoon-race-1920.jpg b/docs/static/courses/ucp-science/spoon-race/egg-and-spoon-race-1920.jpg new file mode 100644 index 00000000000..7756942a3f4 Binary files /dev/null and b/docs/static/courses/ucp-science/spoon-race/egg-and-spoon-race-1920.jpg differ diff --git a/docs/static/courses/ucp-science/spoon-race/extension b/docs/static/courses/ucp-science/spoon-race/extension new file mode 100644 index 00000000000..5cb0d01317f Binary files /dev/null and b/docs/static/courses/ucp-science/spoon-race/extension differ diff --git a/docs/static/courses/ucp-science/spoon-race/extension.png b/docs/static/courses/ucp-science/spoon-race/extension.png new file mode 100644 index 00000000000..ef729d6cee6 Binary files /dev/null and b/docs/static/courses/ucp-science/spoon-race/extension.png differ diff --git a/docs/static/courses/ucp-science/spoon-race/microbit-accelerometer.png b/docs/static/courses/ucp-science/spoon-race/microbit-accelerometer.png new file mode 100644 index 00000000000..af956b8bec0 Binary files /dev/null and b/docs/static/courses/ucp-science/spoon-race/microbit-accelerometer.png differ diff --git a/docs/static/courses/ucp-science/spoon-race/microbit-axis.png b/docs/static/courses/ucp-science/spoon-race/microbit-axis.png new file mode 100644 index 00000000000..ff956aa76be Binary files /dev/null and b/docs/static/courses/ucp-science/spoon-race/microbit-axis.png differ diff --git a/docs/static/courses/ucp-science/spoon-race/my-data-graph.png b/docs/static/courses/ucp-science/spoon-race/my-data-graph.png new file mode 100644 index 00000000000..a22337ec02a Binary files /dev/null and b/docs/static/courses/ucp-science/spoon-race/my-data-graph.png differ diff --git a/docs/static/courses/ucp-science/spoon-race/my-data-htm.png b/docs/static/courses/ucp-science/spoon-race/my-data-htm.png new file mode 100644 index 00000000000..03c8b5046e2 Binary files /dev/null and b/docs/static/courses/ucp-science/spoon-race/my-data-htm.png differ diff --git a/docs/static/courses/ucp-science/spoon-race/my-data-table.png b/docs/static/courses/ucp-science/spoon-race/my-data-table.png new file mode 100644 index 00000000000..ce1f522c36f Binary files /dev/null and b/docs/static/courses/ucp-science/spoon-race/my-data-table.png differ diff --git a/docs/static/courses/ucp-science/spoon-race/sim-data.gif b/docs/static/courses/ucp-science/spoon-race/sim-data.gif new file mode 100644 index 00000000000..6d94c0a4809 Binary files /dev/null and b/docs/static/courses/ucp-science/spoon-race/sim-data.gif differ diff --git a/docs/static/courses/ucp-science/spoon-race/simulator-xyz-accel.png b/docs/static/courses/ucp-science/spoon-race/simulator-xyz-accel.png new file mode 100644 index 00000000000..86868103c16 Binary files /dev/null and b/docs/static/courses/ucp-science/spoon-race/simulator-xyz-accel.png differ diff --git a/docs/static/courses/ucp-science/spoon-race/simulator.png b/docs/static/courses/ucp-science/spoon-race/simulator.png new file mode 100644 index 00000000000..1b140349c02 Binary files /dev/null and b/docs/static/courses/ucp-science/spoon-race/simulator.png differ diff --git a/docs/static/courses/ucp-science/spoon-race/spoon-1.jpg b/docs/static/courses/ucp-science/spoon-race/spoon-1.jpg new file mode 100644 index 00000000000..745fd0c7e1e Binary files /dev/null and b/docs/static/courses/ucp-science/spoon-race/spoon-1.jpg differ diff --git a/docs/static/courses/ucp-science/spoon-race/spoon-2.jpg b/docs/static/courses/ucp-science/spoon-race/spoon-2.jpg new file mode 100644 index 00000000000..87531ec2208 Binary files /dev/null and b/docs/static/courses/ucp-science/spoon-race/spoon-2.jpg differ diff --git a/docs/static/courses/ucp-science/spoon-race/spoon-3.jpg b/docs/static/courses/ucp-science/spoon-race/spoon-3.jpg new file mode 100644 index 00000000000..328dbef416d Binary files /dev/null and b/docs/static/courses/ucp-science/spoon-race/spoon-3.jpg differ diff --git a/docs/static/courses/ucp-science/spoon-race/spreadsheet-1.png b/docs/static/courses/ucp-science/spoon-race/spreadsheet-1.png new file mode 100644 index 00000000000..fbf0e7bb8d5 Binary files /dev/null and b/docs/static/courses/ucp-science/spoon-race/spreadsheet-1.png differ diff --git a/docs/static/courses/ucp-science/spoon-race/spreadsheet-2.png b/docs/static/courses/ucp-science/spoon-race/spreadsheet-2.png new file mode 100644 index 00000000000..8e8604c4cf1 Binary files /dev/null and b/docs/static/courses/ucp-science/spoon-race/spreadsheet-2.png differ diff --git a/docs/static/courses/ucp-science/spoon-race/spreadsheet-3.png b/docs/static/courses/ucp-science/spoon-race/spreadsheet-3.png new file mode 100644 index 00000000000..3aa3e5e101b Binary files /dev/null and b/docs/static/courses/ucp-science/spoon-race/spreadsheet-3.png differ diff --git a/docs/static/courses/ucp-science/spoon-race/spreadsheet-4.png b/docs/static/courses/ucp-science/spoon-race/spreadsheet-4.png new file mode 100644 index 00000000000..6b1e5744774 Binary files /dev/null and b/docs/static/courses/ucp-science/spoon-race/spreadsheet-4.png differ diff --git a/docs/static/courses/ucp-science/spoon-race/spreadsheet-5.png b/docs/static/courses/ucp-science/spoon-race/spreadsheet-5.png new file mode 100644 index 00000000000..c35ee3bbf08 Binary files /dev/null and b/docs/static/courses/ucp-science/spoon-race/spreadsheet-5.png differ diff --git a/docs/static/deep-dive/playlist.png b/docs/static/deep-dive/playlist.png new file mode 100644 index 00000000000..f32312b20ce Binary files /dev/null and b/docs/static/deep-dive/playlist.png differ diff --git a/docs/static/device/compass/compass-align.png b/docs/static/device/compass/compass-align.png new file mode 100644 index 00000000000..27a73c97f29 Binary files /dev/null and b/docs/static/device/compass/compass-align.png differ diff --git a/docs/static/device/compass/compass-degrees.png b/docs/static/device/compass/compass-degrees.png new file mode 100644 index 00000000000..c9dde3dcde2 Binary files /dev/null and b/docs/static/device/compass/compass-degrees.png differ diff --git a/docs/static/device/compass/compass-needle.png b/docs/static/device/compass/compass-needle.png new file mode 100644 index 00000000000..8145ad098b2 Binary files /dev/null and b/docs/static/device/compass/compass-needle.png differ diff --git a/docs/static/device/compass/plastic-compass.jpg b/docs/static/device/compass/plastic-compass.jpg new file mode 100644 index 00000000000..33c7e48fc03 Binary files /dev/null and b/docs/static/device/compass/plastic-compass.jpg differ diff --git a/docs/static/download/browser-unpair-image.gif b/docs/static/download/browser-unpair-image.gif new file mode 100644 index 00000000000..ab08b2f484a Binary files /dev/null and b/docs/static/download/browser-unpair-image.gif differ diff --git a/docs/static/download/connect-microbit.gif b/docs/static/download/connect-microbit.gif new file mode 100644 index 00000000000..531d5c31f85 Binary files /dev/null and b/docs/static/download/connect-microbit.gif differ diff --git a/docs/static/download/connect.png b/docs/static/download/connect.png index b2354b4f923..b9e51a19eff 100644 Binary files a/docs/static/download/connect.png and b/docs/static/download/connect.png differ diff --git a/docs/static/download/connected.png b/docs/static/download/connected.png new file mode 100644 index 00000000000..c8adc5d9ff5 Binary files /dev/null and b/docs/static/download/connected.png differ diff --git a/docs/static/download/device-forgotten.gif b/docs/static/download/device-forgotten.gif new file mode 100644 index 00000000000..dd759c7e39f Binary files /dev/null and b/docs/static/download/device-forgotten.gif differ diff --git a/docs/static/download/firmware.png b/docs/static/download/firmware.png index c2280fb2c8e..87ce2f19a06 100644 Binary files a/docs/static/download/firmware.png and b/docs/static/download/firmware.png differ diff --git a/docs/static/download/full-reset.gif b/docs/static/download/full-reset.gif new file mode 100644 index 00000000000..5052d4e0161 Binary files /dev/null and b/docs/static/download/full-reset.gif differ diff --git a/docs/static/download/incompatible.png b/docs/static/download/incompatible.png new file mode 100644 index 00000000000..1ae40aeeacc Binary files /dev/null and b/docs/static/download/incompatible.png differ diff --git a/docs/static/download/pair-browser.png b/docs/static/download/pair-browser.png new file mode 100644 index 00000000000..451a7b7954a Binary files /dev/null and b/docs/static/download/pair-browser.png differ diff --git a/docs/static/download/pair.png b/docs/static/download/pair.png index 1dcf58519ff..e648aa7dfe6 100644 Binary files a/docs/static/download/pair.png and b/docs/static/download/pair.png differ diff --git a/docs/static/download/selecting-microbit.gif b/docs/static/download/selecting-microbit.gif new file mode 100644 index 00000000000..8b42ea0666d Binary files /dev/null and b/docs/static/download/selecting-microbit.gif differ diff --git a/docs/static/download/successfully-paired.png b/docs/static/download/successfully-paired.png new file mode 100644 index 00000000000..87ce2f19a06 Binary files /dev/null and b/docs/static/download/successfully-paired.png differ diff --git a/docs/static/download/transfer.png b/docs/static/download/transfer.png index fb2c71563b5..4ac41b59200 100644 Binary files a/docs/static/download/transfer.png and b/docs/static/download/transfer.png differ diff --git a/docs/static/experiments/blocksErrorList.png b/docs/static/experiments/blocksErrorList.png new file mode 100644 index 00000000000..380d1c7243c Binary files /dev/null and b/docs/static/experiments/blocksErrorList.png differ diff --git a/docs/static/experiments/debugExtensionCode.png b/docs/static/experiments/debugExtensionCode.png new file mode 100644 index 00000000000..0ec17c36e5a Binary files /dev/null and b/docs/static/experiments/debugExtensionCode.png differ diff --git a/docs/static/experiments/forceenableaierrorhelp.png b/docs/static/experiments/forceenableaierrorhelp.png new file mode 100644 index 00000000000..fe09686ddbf Binary files /dev/null and b/docs/static/experiments/forceenableaierrorhelp.png differ diff --git a/docs/static/extensions/edit-settings-button.png b/docs/static/extensions/edit-settings-button.png new file mode 100644 index 00000000000..fe59f3d8683 Binary files /dev/null and b/docs/static/extensions/edit-settings-button.png differ diff --git a/docs/static/extensions/extension-blocks.png b/docs/static/extensions/extension-blocks.png new file mode 100644 index 00000000000..c7fedaaf71a Binary files /dev/null and b/docs/static/extensions/extension-blocks.png differ diff --git a/docs/static/extensions/extensions-window.gif b/docs/static/extensions/extensions-window.gif new file mode 100644 index 00000000000..4313bedc25f Binary files /dev/null and b/docs/static/extensions/extensions-window.gif differ diff --git a/docs/static/extensions/file-explorer.png b/docs/static/extensions/file-explorer.png new file mode 100644 index 00000000000..3b392322017 Binary files /dev/null and b/docs/static/extensions/file-explorer.png differ diff --git a/docs/static/extensions/new-extension.png b/docs/static/extensions/new-extension.png new file mode 100644 index 00000000000..e4b54d14ba7 Binary files /dev/null and b/docs/static/extensions/new-extension.png differ diff --git a/docs/static/extensions/settings-menu.png b/docs/static/extensions/settings-menu.png new file mode 100644 index 00000000000..1d9cdbaae0c Binary files /dev/null and b/docs/static/extensions/settings-menu.png differ diff --git a/docs/static/extensions/toolbox-category.png b/docs/static/extensions/toolbox-category.png new file mode 100644 index 00000000000..b9141393e74 Binary files /dev/null and b/docs/static/extensions/toolbox-category.png differ diff --git a/docs/static/herogallery/behind-makecode-hardware.png b/docs/static/herogallery/behind-makecode-hardware.png new file mode 100644 index 00000000000..68037757865 Binary files /dev/null and b/docs/static/herogallery/behind-makecode-hardware.png differ diff --git a/docs/static/herogallery/hero-banner.png b/docs/static/herogallery/hero-banner.png new file mode 100644 index 00000000000..27137e90637 Binary files /dev/null and b/docs/static/herogallery/hero-banner.png differ diff --git a/docs/static/herogallery/intro-to-microbit.png b/docs/static/herogallery/intro-to-microbit.png new file mode 100644 index 00000000000..84e09de63f5 Binary files /dev/null and b/docs/static/herogallery/intro-to-microbit.png differ diff --git a/docs/static/herogallery/microbit-createai.png b/docs/static/herogallery/microbit-createai.png new file mode 100644 index 00000000000..d9ed19ac623 Binary files /dev/null and b/docs/static/herogallery/microbit-createai.png differ diff --git a/docs/static/herogallery/send-messages-radio.png b/docs/static/herogallery/send-messages-radio.png new file mode 100644 index 00000000000..a6dd0909235 Binary files /dev/null and b/docs/static/herogallery/send-messages-radio.png differ diff --git a/docs/static/herogallery/soil-moisture.png b/docs/static/herogallery/soil-moisture.png new file mode 100644 index 00000000000..23bc9c374cf Binary files /dev/null and b/docs/static/herogallery/soil-moisture.png differ diff --git a/docs/static/icons/immersive-reader-light.svg b/docs/static/icons/immersive-reader-light.svg new file mode 100644 index 00000000000..359ca9b86c3 --- /dev/null +++ b/docs/static/icons/immersive-reader-light.svg @@ -0,0 +1,4 @@ + + + + diff --git a/docs/static/icons/immersive-reader.svg b/docs/static/icons/immersive-reader.svg new file mode 100644 index 00000000000..fbab22351f0 --- /dev/null +++ b/docs/static/icons/immersive-reader.svg @@ -0,0 +1,4 @@ + + + + diff --git a/docs/static/icons/maskable-icon-640x640.png b/docs/static/icons/maskable-icon-640x640.png new file mode 100644 index 00000000000..c147b014964 Binary files /dev/null and b/docs/static/icons/maskable-icon-640x640.png differ diff --git a/docs/static/identity/cloud-projects.png b/docs/static/identity/cloud-projects.png new file mode 100644 index 00000000000..7527e34bf12 Binary files /dev/null and b/docs/static/identity/cloud-projects.png differ diff --git a/docs/static/identity/project-cards.png b/docs/static/identity/project-cards.png new file mode 100644 index 00000000000..599bac833e4 Binary files /dev/null and b/docs/static/identity/project-cards.png differ diff --git a/docs/static/identity/saving.gif b/docs/static/identity/saving.gif new file mode 100644 index 00000000000..127641cc06a Binary files /dev/null and b/docs/static/identity/saving.gif differ diff --git a/docs/static/jacdac/button-smasher.jpg b/docs/static/jacdac/button-smasher.jpg new file mode 100644 index 00000000000..8647705bb28 Binary files /dev/null and b/docs/static/jacdac/button-smasher.jpg differ diff --git a/docs/static/jacdac/getting-started.jpg b/docs/static/jacdac/getting-started.jpg new file mode 100644 index 00000000000..5ce8749eeeb Binary files /dev/null and b/docs/static/jacdac/getting-started.jpg differ diff --git a/docs/static/jacdac/light-sound-bender.jpg b/docs/static/jacdac/light-sound-bender.jpg new file mode 100644 index 00000000000..ac45ecd7069 Binary files /dev/null and b/docs/static/jacdac/light-sound-bender.jpg differ diff --git a/docs/static/jacdac/magnetic-sound-bender.jpg b/docs/static/jacdac/magnetic-sound-bender.jpg new file mode 100644 index 00000000000..3b139ef7878 Binary files /dev/null and b/docs/static/jacdac/magnetic-sound-bender.jpg differ diff --git a/docs/static/jacdac/rotary-sound-bender.jpg b/docs/static/jacdac/rotary-sound-bender.jpg new file mode 100644 index 00000000000..cdb08eace14 Binary files /dev/null and b/docs/static/jacdac/rotary-sound-bender.jpg differ diff --git a/docs/static/jacdac/slider-sound-bender.jpg b/docs/static/jacdac/slider-sound-bender.jpg new file mode 100644 index 00000000000..30d00d12ab8 Binary files /dev/null and b/docs/static/jacdac/slider-sound-bender.jpg differ diff --git a/docs/static/jacdac/sound-led.jpg b/docs/static/jacdac/sound-led.jpg new file mode 100644 index 00000000000..04a60d62c50 Binary files /dev/null and b/docs/static/jacdac/sound-led.jpg differ diff --git a/docs/static/libs/audio-recording.png b/docs/static/libs/audio-recording.png new file mode 100644 index 00000000000..6206d7e7929 Binary files /dev/null and b/docs/static/libs/audio-recording.png differ diff --git a/docs/static/libs/color.png b/docs/static/libs/color.png new file mode 100644 index 00000000000..8f99e7871b7 Binary files /dev/null and b/docs/static/libs/color.png differ diff --git a/docs/static/libs/datalogger.png b/docs/static/libs/datalogger.png new file mode 100644 index 00000000000..0703ee5e0cc Binary files /dev/null and b/docs/static/libs/datalogger.png differ diff --git a/docs/static/libs/flashlog.png b/docs/static/libs/flashlog.png new file mode 100644 index 00000000000..f049b5a03b5 Binary files /dev/null and b/docs/static/libs/flashlog.png differ diff --git a/docs/static/libs/radio-broadcast.png b/docs/static/libs/radio-broadcast.png index c8cca2a5026..d89092d6f61 100644 Binary files a/docs/static/libs/radio-broadcast.png and b/docs/static/libs/radio-broadcast.png differ diff --git a/docs/static/libs/servo.png b/docs/static/libs/servo.png index c12b1badea2..7ae30013e6a 100644 Binary files a/docs/static/libs/servo.png and b/docs/static/libs/servo.png differ diff --git a/docs/static/mb/device-v2.jpg b/docs/static/mb/device-v2.jpg new file mode 100644 index 00000000000..31339c4db73 Binary files /dev/null and b/docs/static/mb/device-v2.jpg differ diff --git a/docs/static/mb/device/details-243.png b/docs/static/mb/device/details-243.png index 8f3d7c035e5..61ed5f035b4 100644 Binary files a/docs/static/mb/device/details-243.png and b/docs/static/mb/device/details-243.png differ diff --git a/docs/static/mb/device/pins-v1-v2.png b/docs/static/mb/device/pins-v1-v2.png new file mode 100644 index 00000000000..aa0ba7aee33 Binary files /dev/null and b/docs/static/mb/device/pins-v1-v2.png differ diff --git a/docs/static/mb/device/usb-generic.jpg b/docs/static/mb/device/usb-generic.jpg deleted file mode 100644 index 13a782b7647..00000000000 Binary files a/docs/static/mb/device/usb-generic.jpg and /dev/null differ diff --git a/docs/static/mb/device/usb-mac.jpg b/docs/static/mb/device/usb-mac.jpg deleted file mode 100644 index 8b6af443d1d..00000000000 Binary files a/docs/static/mb/device/usb-mac.jpg and /dev/null differ diff --git a/docs/static/mb/device/usb-windows-edge-1.png b/docs/static/mb/device/usb-windows-edge-1.png deleted file mode 100644 index 07831fdc8e1..00000000000 Binary files a/docs/static/mb/device/usb-windows-edge-1.png and /dev/null differ diff --git a/docs/static/mb/device/usb-windows-firefox-1.png b/docs/static/mb/device/usb-windows-firefox-1.png deleted file mode 100644 index 83a7c17d674..00000000000 Binary files a/docs/static/mb/device/usb-windows-firefox-1.png and /dev/null differ diff --git a/docs/static/mb/device/usb-windows-ie11-1.png b/docs/static/mb/device/usb-windows-ie11-1.png deleted file mode 100644 index 5a3e3bfdd46..00000000000 Binary files a/docs/static/mb/device/usb-windows-ie11-1.png and /dev/null differ diff --git a/docs/static/mb/device/usb/connect-usb.png b/docs/static/mb/device/usb/connect-usb.png new file mode 100644 index 00000000000..3083ac91b44 Binary files /dev/null and b/docs/static/mb/device/usb/connect-usb.png differ diff --git a/docs/static/mb/device/usb/download-button-menu.png b/docs/static/mb/device/usb/download-button-menu.png new file mode 100644 index 00000000000..9a9653ac3b2 Binary files /dev/null and b/docs/static/mb/device/usb/download-button-menu.png differ diff --git a/docs/static/mb/device/usb/no-pair-device.png b/docs/static/mb/device/usb/no-pair-device.png new file mode 100644 index 00000000000..74780e399b6 Binary files /dev/null and b/docs/static/mb/device/usb/no-pair-device.png differ diff --git a/docs/static/mb/device/usb/pair-device.png b/docs/static/mb/device/usb/pair-device.png new file mode 100644 index 00000000000..2faad2360ee Binary files /dev/null and b/docs/static/mb/device/usb/pair-device.png differ diff --git a/docs/static/mb/device/usb/select-device-pair.png b/docs/static/mb/device/usb/select-device-pair.png new file mode 100644 index 00000000000..e748d0767e9 Binary files /dev/null and b/docs/static/mb/device/usb/select-device-pair.png differ diff --git a/docs/static/mb/device/usb/usb-connect-fail.png b/docs/static/mb/device/usb/usb-connect-fail.png new file mode 100644 index 00000000000..7707451dc3a Binary files /dev/null and b/docs/static/mb/device/usb/usb-connect-fail.png differ diff --git a/docs/static/mb/device/usb/usb-connected.png b/docs/static/mb/device/usb/usb-connected.png new file mode 100644 index 00000000000..70c1b8eea43 Binary files /dev/null and b/docs/static/mb/device/usb/usb-connected.png differ diff --git a/docs/static/mb/homepage-content-example.jpg b/docs/static/mb/homepage-content-example.jpg new file mode 100644 index 00000000000..48cfe3c8e8f Binary files /dev/null and b/docs/static/mb/homepage-content-example.jpg differ diff --git a/docs/static/mb/homepage-link-example.jpg b/docs/static/mb/homepage-link-example.jpg new file mode 100644 index 00000000000..768352830ec Binary files /dev/null and b/docs/static/mb/homepage-link-example.jpg differ diff --git a/docs/static/mb/projects/a4-motion-v2.png b/docs/static/mb/projects/a4-motion-v2.png new file mode 100644 index 00000000000..ed15d50415d Binary files /dev/null and b/docs/static/mb/projects/a4-motion-v2.png differ diff --git a/docs/static/mb/projects/accel-console.png b/docs/static/mb/projects/accel-console.png new file mode 100644 index 00000000000..e9b2703adf5 Binary files /dev/null and b/docs/static/mb/projects/accel-console.png differ diff --git a/docs/static/mb/projects/blow-away.png b/docs/static/mb/projects/blow-away.png new file mode 100644 index 00000000000..8e8fe5e02c3 Binary files /dev/null and b/docs/static/mb/projects/blow-away.png differ diff --git a/docs/static/mb/projects/cat-napping/11_datafile.png b/docs/static/mb/projects/cat-napping/11_datafile.png new file mode 100644 index 00000000000..18bf917fe61 Binary files /dev/null and b/docs/static/mb/projects/cat-napping/11_datafile.png differ diff --git a/docs/static/mb/projects/cat-napping/11_mydata.png b/docs/static/mb/projects/cat-napping/11_mydata.png new file mode 100644 index 00000000000..f028a9e25d2 Binary files /dev/null and b/docs/static/mb/projects/cat-napping/11_mydata.png differ diff --git a/docs/static/mb/projects/cat-napping/1_lychee.png b/docs/static/mb/projects/cat-napping/1_lychee.png new file mode 100644 index 00000000000..865267c7646 Binary files /dev/null and b/docs/static/mb/projects/cat-napping/1_lychee.png differ diff --git a/docs/static/mb/projects/clap-lights.png b/docs/static/mb/projects/clap-lights.png new file mode 100644 index 00000000000..7d692df59a8 Binary files /dev/null and b/docs/static/mb/projects/clap-lights.png differ diff --git a/docs/static/mb/projects/countdown.png b/docs/static/mb/projects/countdown.png new file mode 100644 index 00000000000..7749fc15f90 Binary files /dev/null and b/docs/static/mb/projects/countdown.png differ diff --git a/docs/static/mb/projects/dance-beat.png b/docs/static/mb/projects/dance-beat.png new file mode 100644 index 00000000000..7d692df59a8 Binary files /dev/null and b/docs/static/mb/projects/dance-beat.png differ diff --git a/docs/static/mb/projects/dance-card.png b/docs/static/mb/projects/dance-card.png new file mode 100644 index 00000000000..18a5e7164d1 Binary files /dev/null and b/docs/static/mb/projects/dance-card.png differ diff --git a/docs/static/mb/projects/electric-guitar.png b/docs/static/mb/projects/electric-guitar.png new file mode 100644 index 00000000000..ac3b47b705a Binary files /dev/null and b/docs/static/mb/projects/electric-guitar.png differ diff --git a/docs/static/mb/projects/electric-guitar/connections.jpg b/docs/static/mb/projects/electric-guitar/connections.jpg new file mode 100644 index 00000000000..4d58cc9d8de Binary files /dev/null and b/docs/static/mb/projects/electric-guitar/connections.jpg differ diff --git a/docs/static/mb/projects/electric-guitar/electric-guitar.png b/docs/static/mb/projects/electric-guitar/electric-guitar.png new file mode 100644 index 00000000000..ac3b47b705a Binary files /dev/null and b/docs/static/mb/projects/electric-guitar/electric-guitar.png differ diff --git a/docs/static/mb/projects/electric-guitar/guitar-board1.jpg b/docs/static/mb/projects/electric-guitar/guitar-board1.jpg new file mode 100644 index 00000000000..b395a5baeb0 Binary files /dev/null and b/docs/static/mb/projects/electric-guitar/guitar-board1.jpg differ diff --git a/docs/static/mb/projects/electric-guitar/guitar-board2.jpg b/docs/static/mb/projects/electric-guitar/guitar-board2.jpg new file mode 100644 index 00000000000..973f02548f6 Binary files /dev/null and b/docs/static/mb/projects/electric-guitar/guitar-board2.jpg differ diff --git a/docs/static/mb/projects/jonnys-bird.png b/docs/static/mb/projects/jonnys-bird.png new file mode 100644 index 00000000000..149eb94a97b Binary files /dev/null and b/docs/static/mb/projects/jonnys-bird.png differ diff --git a/docs/static/mb/projects/lose.png b/docs/static/mb/projects/lose.png new file mode 100644 index 00000000000..ffaf9a8d844 Binary files /dev/null and b/docs/static/mb/projects/lose.png differ diff --git a/docs/static/mb/projects/micro-coin/coinbank.png b/docs/static/mb/projects/micro-coin/coinbank.png new file mode 100644 index 00000000000..2ff10f0541c Binary files /dev/null and b/docs/static/mb/projects/micro-coin/coinbank.png differ diff --git a/docs/static/mb/projects/morse-chat.png b/docs/static/mb/projects/morse-chat.png new file mode 100644 index 00000000000..c769d402c20 Binary files /dev/null and b/docs/static/mb/projects/morse-chat.png differ diff --git a/docs/static/mb/projects/name-tag/name-tag.gif b/docs/static/mb/projects/name-tag/name-tag.gif index 7abb09b481c..6034b9be234 100644 Binary files a/docs/static/mb/projects/name-tag/name-tag.gif and b/docs/static/mb/projects/name-tag/name-tag.gif differ diff --git a/docs/static/mb/projects/octobot.jpg b/docs/static/mb/projects/octobot.jpg new file mode 100644 index 00000000000..8d360b0eaf5 Binary files /dev/null and b/docs/static/mb/projects/octobot.jpg differ diff --git a/docs/static/mb/projects/p0.png b/docs/static/mb/projects/p0.png new file mode 100644 index 00000000000..596fffb08eb Binary files /dev/null and b/docs/static/mb/projects/p0.png differ diff --git a/docs/static/mb/projects/pet-hamster.png b/docs/static/mb/projects/pet-hamster.png new file mode 100644 index 00000000000..4afc8f359ae Binary files /dev/null and b/docs/static/mb/projects/pet-hamster.png differ diff --git a/docs/static/mb/projects/points.png b/docs/static/mb/projects/points.png new file mode 100644 index 00000000000..744b552024c Binary files /dev/null and b/docs/static/mb/projects/points.png differ diff --git a/docs/static/mb/projects/rock-paper-scissors.jpg b/docs/static/mb/projects/rock-paper-scissors.jpg new file mode 100644 index 00000000000..5ac0567b66e Binary files /dev/null and b/docs/static/mb/projects/rock-paper-scissors.jpg differ diff --git a/docs/static/mb/projects/rock-paper-scissors/attach-mb.jpg b/docs/static/mb/projects/rock-paper-scissors/attach-mb.jpg new file mode 100644 index 00000000000..e64bc6b4b71 Binary files /dev/null and b/docs/static/mb/projects/rock-paper-scissors/attach-mb.jpg differ diff --git a/docs/static/mb/projects/rock-paper-scissors/cut-roll-tape.jpg b/docs/static/mb/projects/rock-paper-scissors/cut-roll-tape.jpg new file mode 100644 index 00000000000..7fe6a9c7f1f Binary files /dev/null and b/docs/static/mb/projects/rock-paper-scissors/cut-roll-tape.jpg differ diff --git a/docs/static/mb/projects/rock-paper-scissors/wrist-fastener.jpg b/docs/static/mb/projects/rock-paper-scissors/wrist-fastener.jpg new file mode 100644 index 00000000000..ab3bbe144b3 Binary files /dev/null and b/docs/static/mb/projects/rock-paper-scissors/wrist-fastener.jpg differ diff --git a/docs/static/mb/projects/shake.png b/docs/static/mb/projects/shake.png new file mode 100644 index 00000000000..879cc44abb6 Binary files /dev/null and b/docs/static/mb/projects/shake.png differ diff --git a/docs/static/mb/projects/twoplayermaze.jpg b/docs/static/mb/projects/twoplayermaze.jpg new file mode 100644 index 00000000000..356a0f116c6 Binary files /dev/null and b/docs/static/mb/projects/twoplayermaze.jpg differ diff --git a/docs/static/mb/translate/crowdin-folder.png b/docs/static/mb/translate/crowdin-folder.png new file mode 100644 index 00000000000..cd11d69a2ef Binary files /dev/null and b/docs/static/mb/translate/crowdin-folder.png differ diff --git a/docs/static/microbit-org/createai/activity-timer.png b/docs/static/microbit-org/createai/activity-timer.png new file mode 100644 index 00000000000..eb9de727fbe Binary files /dev/null and b/docs/static/microbit-org/createai/activity-timer.png differ diff --git a/docs/static/microbit-org/createai/more-about.png b/docs/static/microbit-org/createai/more-about.png new file mode 100644 index 00000000000..cfa5aa80bea Binary files /dev/null and b/docs/static/microbit-org/createai/more-about.png differ diff --git a/docs/static/microbit-org/createai/simple-exercise-timer.png b/docs/static/microbit-org/createai/simple-exercise-timer.png new file mode 100644 index 00000000000..188f63a15ae Binary files /dev/null and b/docs/static/microbit-org/createai/simple-exercise-timer.png differ diff --git a/docs/static/microbit-org/createai/storytelling-friend.png b/docs/static/microbit-org/createai/storytelling-friend.png new file mode 100644 index 00000000000..f5f8a0107cb Binary files /dev/null and b/docs/static/microbit-org/createai/storytelling-friend.png differ diff --git a/docs/static/microbit-org/data-logging/environment.png b/docs/static/microbit-org/data-logging/environment.png new file mode 100644 index 00000000000..53018dd2aba Binary files /dev/null and b/docs/static/microbit-org/data-logging/environment.png differ diff --git a/docs/static/microbit-org/data-logging/kick-strength.png b/docs/static/microbit-org/data-logging/kick-strength.png new file mode 100644 index 00000000000..1fdc97f6afe Binary files /dev/null and b/docs/static/microbit-org/data-logging/kick-strength.png differ diff --git a/docs/static/microbit-org/data-logging/movement.png b/docs/static/microbit-org/data-logging/movement.png new file mode 100644 index 00000000000..2415ec78b40 Binary files /dev/null and b/docs/static/microbit-org/data-logging/movement.png differ diff --git a/docs/static/microbit-org/data-logging/solar-panel.png b/docs/static/microbit-org/data-logging/solar-panel.png new file mode 100644 index 00000000000..7d0347f9d53 Binary files /dev/null and b/docs/static/microbit-org/data-logging/solar-panel.png differ diff --git a/docs/static/microbit-org/data-logging/traffic-survey.png b/docs/static/microbit-org/data-logging/traffic-survey.png new file mode 100644 index 00000000000..16e6a2308a3 Binary files /dev/null and b/docs/static/microbit-org/data-logging/traffic-survey.png differ diff --git a/docs/static/microbit-org/feature-videos/accelerometer.png b/docs/static/microbit-org/feature-videos/accelerometer.png new file mode 100644 index 00000000000..e2825891547 Binary files /dev/null and b/docs/static/microbit-org/feature-videos/accelerometer.png differ diff --git a/docs/static/microbit-org/feature-videos/buttons.png b/docs/static/microbit-org/feature-videos/buttons.png new file mode 100644 index 00000000000..e7843919400 Binary files /dev/null and b/docs/static/microbit-org/feature-videos/buttons.png differ diff --git a/docs/static/microbit-org/feature-videos/full-playlist.png b/docs/static/microbit-org/feature-videos/full-playlist.png new file mode 100644 index 00000000000..fa347344c75 Binary files /dev/null and b/docs/static/microbit-org/feature-videos/full-playlist.png differ diff --git a/docs/static/microbit-org/feature-videos/input-output.png b/docs/static/microbit-org/feature-videos/input-output.png new file mode 100644 index 00000000000..e49000114df Binary files /dev/null and b/docs/static/microbit-org/feature-videos/input-output.png differ diff --git a/docs/static/microbit-org/feature-videos/introduction.png b/docs/static/microbit-org/feature-videos/introduction.png new file mode 100644 index 00000000000..c3254a2be51 Binary files /dev/null and b/docs/static/microbit-org/feature-videos/introduction.png differ diff --git a/docs/static/microbit-org/feature-videos/leds.png b/docs/static/microbit-org/feature-videos/leds.png new file mode 100644 index 00000000000..08b33d151fb Binary files /dev/null and b/docs/static/microbit-org/feature-videos/leds.png differ diff --git a/docs/static/microbit-org/feature-videos/processor.png b/docs/static/microbit-org/feature-videos/processor.png new file mode 100644 index 00000000000..c428ddf6063 Binary files /dev/null and b/docs/static/microbit-org/feature-videos/processor.png differ diff --git a/docs/static/microbit-org/first-lessons/beating-heart.png b/docs/static/microbit-org/first-lessons/beating-heart.png new file mode 100644 index 00000000000..f669c032293 Binary files /dev/null and b/docs/static/microbit-org/first-lessons/beating-heart.png differ diff --git a/docs/static/microbit-org/first-lessons/emotion-badge.png b/docs/static/microbit-org/first-lessons/emotion-badge.png new file mode 100644 index 00000000000..04dd4f8665d Binary files /dev/null and b/docs/static/microbit-org/first-lessons/emotion-badge.png differ diff --git a/docs/static/microbit-org/first-lessons/name-badge.png b/docs/static/microbit-org/first-lessons/name-badge.png new file mode 100644 index 00000000000..3a13fc034e7 Binary files /dev/null and b/docs/static/microbit-org/first-lessons/name-badge.png differ diff --git a/docs/static/microbit-org/first-lessons/nightlight.png b/docs/static/microbit-org/first-lessons/nightlight.png new file mode 100644 index 00000000000..c08c5f497b8 Binary files /dev/null and b/docs/static/microbit-org/first-lessons/nightlight.png differ diff --git a/docs/static/microbit-org/first-lessons/overview.png b/docs/static/microbit-org/first-lessons/overview.png new file mode 100644 index 00000000000..82f6b6a4bda Binary files /dev/null and b/docs/static/microbit-org/first-lessons/overview.png differ diff --git a/docs/static/microbit-org/first-lessons/rock-paper-scissors.png b/docs/static/microbit-org/first-lessons/rock-paper-scissors.png new file mode 100644 index 00000000000..bb7036b5843 Binary files /dev/null and b/docs/static/microbit-org/first-lessons/rock-paper-scissors.png differ diff --git a/docs/static/microbit-org/first-lessons/step-counter.png b/docs/static/microbit-org/first-lessons/step-counter.png new file mode 100644 index 00000000000..2db890b7b3f Binary files /dev/null and b/docs/static/microbit-org/first-lessons/step-counter.png differ diff --git a/docs/static/microbit-org/make-it-code-it/activity-picker.png b/docs/static/microbit-org/make-it-code-it/activity-picker.png new file mode 100644 index 00000000000..c71df9426b8 Binary files /dev/null and b/docs/static/microbit-org/make-it-code-it/activity-picker.png differ diff --git a/docs/static/microbit-org/make-it-code-it/calming-leds.png b/docs/static/microbit-org/make-it-code-it/calming-leds.png new file mode 100644 index 00000000000..c66747eace3 Binary files /dev/null and b/docs/static/microbit-org/make-it-code-it/calming-leds.png differ diff --git a/docs/static/microbit-org/make-it-code-it/dance-steps.png b/docs/static/microbit-org/make-it-code-it/dance-steps.png new file mode 100644 index 00000000000..44bc320ef07 Binary files /dev/null and b/docs/static/microbit-org/make-it-code-it/dance-steps.png differ diff --git a/docs/static/microbit-org/make-it-code-it/distance-calculator.png b/docs/static/microbit-org/make-it-code-it/distance-calculator.png new file mode 100644 index 00000000000..7b1f586bef2 Binary files /dev/null and b/docs/static/microbit-org/make-it-code-it/distance-calculator.png differ diff --git a/docs/static/microbit-org/make-it-code-it/funny-voice.png b/docs/static/microbit-org/make-it-code-it/funny-voice.png new file mode 100644 index 00000000000..93d451d0e76 Binary files /dev/null and b/docs/static/microbit-org/make-it-code-it/funny-voice.png differ diff --git a/docs/static/microbit-org/make-it-code-it/poetry-generator.png b/docs/static/microbit-org/make-it-code-it/poetry-generator.png new file mode 100644 index 00000000000..4be37e130de Binary files /dev/null and b/docs/static/microbit-org/make-it-code-it/poetry-generator.png differ diff --git a/docs/static/microbit-org/professional-development/all-courses.png b/docs/static/microbit-org/professional-development/all-courses.png new file mode 100644 index 00000000000..a421e2ff201 Binary files /dev/null and b/docs/static/microbit-org/professional-development/all-courses.png differ diff --git a/docs/static/microbit-org/professional-development/first-lessons.png b/docs/static/microbit-org/professional-development/first-lessons.png new file mode 100644 index 00000000000..88c5f6cc650 Binary files /dev/null and b/docs/static/microbit-org/professional-development/first-lessons.png differ diff --git a/docs/static/microbit-org/professional-development/gesture-movement.png b/docs/static/microbit-org/professional-development/gesture-movement.png new file mode 100644 index 00000000000..da837674602 Binary files /dev/null and b/docs/static/microbit-org/professional-development/gesture-movement.png differ diff --git a/docs/static/microbit-org/professional-development/introducing-loops.png b/docs/static/microbit-org/professional-development/introducing-loops.png new file mode 100644 index 00000000000..e26938247bc Binary files /dev/null and b/docs/static/microbit-org/professional-development/introducing-loops.png differ diff --git a/docs/static/microbit-org/professional-development/practical-tips.png b/docs/static/microbit-org/professional-development/practical-tips.png new file mode 100644 index 00000000000..8402ea6a054 Binary files /dev/null and b/docs/static/microbit-org/professional-development/practical-tips.png differ diff --git a/docs/static/microbit-org/professional-development/science-exploration.png b/docs/static/microbit-org/professional-development/science-exploration.png new file mode 100644 index 00000000000..d06643b48f5 Binary files /dev/null and b/docs/static/microbit-org/professional-development/science-exploration.png differ diff --git a/docs/static/microbit-org/professional-development/sensing-making-sound.png b/docs/static/microbit-org/professional-development/sensing-making-sound.png new file mode 100644 index 00000000000..7b4d346bb5a Binary files /dev/null and b/docs/static/microbit-org/professional-development/sensing-making-sound.png differ diff --git a/docs/static/microcode/home.png b/docs/static/microcode/home.png new file mode 100644 index 00000000000..ef4ce4a0e2d Binary files /dev/null and b/docs/static/microcode/home.png differ diff --git a/docs/static/microcode/samples.png b/docs/static/microcode/samples.png new file mode 100644 index 00000000000..64c6b3d6a97 Binary files /dev/null and b/docs/static/microcode/samples.png differ diff --git a/docs/static/microcode/userguide.png b/docs/static/microcode/userguide.png new file mode 100644 index 00000000000..bd80e94a05c Binary files /dev/null and b/docs/static/microcode/userguide.png differ diff --git a/docs/static/orglogowide.png b/docs/static/orglogowide.png new file mode 100644 index 00000000000..d6139e3d0af Binary files /dev/null and b/docs/static/orglogowide.png differ diff --git a/docs/static/profile/microbit-cloud.png b/docs/static/profile/microbit-cloud.png new file mode 100644 index 00000000000..0333f81cb51 Binary files /dev/null and b/docs/static/profile/microbit-cloud.png differ diff --git a/docs/static/providers/clever-logo.png b/docs/static/providers/clever-logo.png new file mode 100644 index 00000000000..3494ce63ad2 Binary files /dev/null and b/docs/static/providers/clever-logo.png differ diff --git a/docs/static/providers/github-mark.png b/docs/static/providers/github-mark.png new file mode 100644 index 00000000000..ea6ff545a24 Binary files /dev/null and b/docs/static/providers/github-mark.png differ diff --git a/docs/static/providers/google-logo.svg b/docs/static/providers/google-logo.svg new file mode 100644 index 00000000000..c06982fbad6 --- /dev/null +++ b/docs/static/providers/google-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/static/providers/microsoft-logo.svg b/docs/static/providers/microsoft-logo.svg new file mode 100644 index 00000000000..5334aa7ca68 --- /dev/null +++ b/docs/static/providers/microsoft-logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/static/share/anon-share-link.png b/docs/static/share/anon-share-link.png new file mode 100644 index 00000000000..7c7fd30e017 Binary files /dev/null and b/docs/static/share/anon-share-link.png differ diff --git a/docs/static/share/edit-shared-project.png b/docs/static/share/edit-shared-project.png new file mode 100644 index 00000000000..f685d47b9b8 Binary files /dev/null and b/docs/static/share/edit-shared-project.png differ diff --git a/docs/static/share/persist-share-link.png b/docs/static/share/persist-share-link.png new file mode 100644 index 00000000000..c6b93f437c7 Binary files /dev/null and b/docs/static/share/persist-share-link.png differ diff --git a/docs/static/share/report-abuse.png b/docs/static/share/report-abuse.png new file mode 100644 index 00000000000..99b56d0103f Binary files /dev/null and b/docs/static/share/report-abuse.png differ diff --git a/docs/static/share/share-embed.png b/docs/static/share/share-embed.png new file mode 100644 index 00000000000..9a877a31724 Binary files /dev/null and b/docs/static/share/share-embed.png differ diff --git a/docs/static/share/share-icon.png b/docs/static/share/share-icon.png new file mode 100644 index 00000000000..8f929a55cfa Binary files /dev/null and b/docs/static/share/share-icon.png differ diff --git a/docs/static/share/share-lms.png b/docs/static/share/share-lms.png new file mode 100644 index 00000000000..e95f6c0dbec Binary files /dev/null and b/docs/static/share/share-lms.png differ diff --git a/docs/static/share/share-project.png b/docs/static/share/share-project.png new file mode 100644 index 00000000000..9879a7400d2 Binary files /dev/null and b/docs/static/share/share-project.png differ diff --git a/docs/static/share/share-qr-code.png b/docs/static/share/share-qr-code.png new file mode 100644 index 00000000000..e6de5703cc8 Binary files /dev/null and b/docs/static/share/share-qr-code.png differ diff --git a/docs/static/share/share-teams.png b/docs/static/share/share-teams.png new file mode 100644 index 00000000000..4bee5bb4b7f Binary files /dev/null and b/docs/static/share/share-teams.png differ diff --git a/docs/static/share/update-link.png b/docs/static/share/update-link.png new file mode 100644 index 00000000000..483e9263d24 Binary files /dev/null and b/docs/static/share/update-link.png differ diff --git a/docs/static/teachertool/add-criteria.png b/docs/static/teachertool/add-criteria.png new file mode 100644 index 00000000000..24f2946b0de Binary files /dev/null and b/docs/static/teachertool/add-criteria.png differ diff --git a/docs/static/teachertool/ask-ai-criteria.png b/docs/static/teachertool/ask-ai-criteria.png new file mode 100644 index 00000000000..764d6a268df Binary files /dev/null and b/docs/static/teachertool/ask-ai-criteria.png differ diff --git a/docs/static/teachertool/autorun-button.png b/docs/static/teachertool/autorun-button.png new file mode 100644 index 00000000000..d33f35ae461 Binary files /dev/null and b/docs/static/teachertool/autorun-button.png differ diff --git a/docs/static/teachertool/checklist-download.png b/docs/static/teachertool/checklist-download.png new file mode 100644 index 00000000000..4f8156ed04f Binary files /dev/null and b/docs/static/teachertool/checklist-download.png differ diff --git a/docs/static/teachertool/checklist-execution.png b/docs/static/teachertool/checklist-execution.png new file mode 100644 index 00000000000..09b76faa893 Binary files /dev/null and b/docs/static/teachertool/checklist-execution.png differ diff --git a/docs/static/teachertool/checklist-name.png b/docs/static/teachertool/checklist-name.png new file mode 100644 index 00000000000..7bf07cf32ae Binary files /dev/null and b/docs/static/teachertool/checklist-name.png differ diff --git a/docs/static/teachertool/criteria-items.png b/docs/static/teachertool/criteria-items.png new file mode 100644 index 00000000000..adc54cd0f2f Binary files /dev/null and b/docs/static/teachertool/criteria-items.png differ diff --git a/docs/static/teachertool/edit-outcome-1.png b/docs/static/teachertool/edit-outcome-1.png new file mode 100644 index 00000000000..6f29f6399fb Binary files /dev/null and b/docs/static/teachertool/edit-outcome-1.png differ diff --git a/docs/static/teachertool/edit-outcome-2.png b/docs/static/teachertool/edit-outcome-2.png new file mode 100644 index 00000000000..a4e2ec1b028 Binary files /dev/null and b/docs/static/teachertool/edit-outcome-2.png differ diff --git a/docs/static/teachertool/editing-results-1.png b/docs/static/teachertool/editing-results-1.png new file mode 100644 index 00000000000..c365dbd307b Binary files /dev/null and b/docs/static/teachertool/editing-results-1.png differ diff --git a/docs/static/teachertool/editing-results-2.png b/docs/static/teachertool/editing-results-2.png new file mode 100644 index 00000000000..322fd9a1c06 Binary files /dev/null and b/docs/static/teachertool/editing-results-2.png differ diff --git a/docs/static/teachertool/editing-results-3.png b/docs/static/teachertool/editing-results-3.png new file mode 100644 index 00000000000..729ebb7c67e Binary files /dev/null and b/docs/static/teachertool/editing-results-3.png differ diff --git a/docs/static/teachertool/export-checklist.png b/docs/static/teachertool/export-checklist.png new file mode 100644 index 00000000000..edb8f07d6ec Binary files /dev/null and b/docs/static/teachertool/export-checklist.png differ diff --git a/docs/static/teachertool/import-checklist-card.png b/docs/static/teachertool/import-checklist-card.png new file mode 100644 index 00000000000..ab3d24736aa Binary files /dev/null and b/docs/static/teachertool/import-checklist-card.png differ diff --git a/docs/static/teachertool/import-checklist-dragdrop-1.png b/docs/static/teachertool/import-checklist-dragdrop-1.png new file mode 100644 index 00000000000..38f31c74d61 Binary files /dev/null and b/docs/static/teachertool/import-checklist-dragdrop-1.png differ diff --git a/docs/static/teachertool/import-checklist-dragdrop-2.png b/docs/static/teachertool/import-checklist-dragdrop-2.png new file mode 100644 index 00000000000..ee69ba66806 Binary files /dev/null and b/docs/static/teachertool/import-checklist-dragdrop-2.png differ diff --git a/docs/static/teachertool/import-checklist-menu.png b/docs/static/teachertool/import-checklist-menu.png new file mode 100644 index 00000000000..13bef36575d Binary files /dev/null and b/docs/static/teachertool/import-checklist-menu.png differ diff --git a/docs/static/teachertool/loaded-project.png b/docs/static/teachertool/loaded-project.png new file mode 100644 index 00000000000..2de8bb49b7f Binary files /dev/null and b/docs/static/teachertool/loaded-project.png differ diff --git a/docs/static/teachertool/new-rubric-from-menu.png b/docs/static/teachertool/new-rubric-from-menu.png new file mode 100644 index 00000000000..a3369af10e5 Binary files /dev/null and b/docs/static/teachertool/new-rubric-from-menu.png differ diff --git a/docs/static/teachertool/new-rubric.png b/docs/static/teachertool/new-rubric.png new file mode 100644 index 00000000000..265b3935962 Binary files /dev/null and b/docs/static/teachertool/new-rubric.png differ diff --git a/docs/static/teachertool/parameters-1.png b/docs/static/teachertool/parameters-1.png new file mode 100644 index 00000000000..76cbdb4a888 Binary files /dev/null and b/docs/static/teachertool/parameters-1.png differ diff --git a/docs/static/teachertool/parameters-2.png b/docs/static/teachertool/parameters-2.png new file mode 100644 index 00000000000..58e960355ea Binary files /dev/null and b/docs/static/teachertool/parameters-2.png differ diff --git a/docs/static/teachertool/parameters-3.png b/docs/static/teachertool/parameters-3.png new file mode 100644 index 00000000000..2741cc0a947 Binary files /dev/null and b/docs/static/teachertool/parameters-3.png differ diff --git a/docs/static/teachertool/prebuilt-rubrics.png b/docs/static/teachertool/prebuilt-rubrics.png new file mode 100644 index 00000000000..dae823d2148 Binary files /dev/null and b/docs/static/teachertool/prebuilt-rubrics.png differ diff --git a/docs/static/teachertool/print-button.png b/docs/static/teachertool/print-button.png new file mode 100644 index 00000000000..d381eb8fdee Binary files /dev/null and b/docs/static/teachertool/print-button.png differ diff --git a/docs/static/teachertool/remove-criteria.png b/docs/static/teachertool/remove-criteria.png new file mode 100644 index 00000000000..b12c466554b Binary files /dev/null and b/docs/static/teachertool/remove-criteria.png differ diff --git a/docs/static/teachertool/run-checklist-button.png b/docs/static/teachertool/run-checklist-button.png new file mode 100644 index 00000000000..f7d766e82cf Binary files /dev/null and b/docs/static/teachertool/run-checklist-button.png differ diff --git a/docs/static/teachertool/split-resize.png b/docs/static/teachertool/split-resize.png new file mode 100644 index 00000000000..32b44e40619 Binary files /dev/null and b/docs/static/teachertool/split-resize.png differ diff --git a/docs/static/teachertool/validate-me.png b/docs/static/teachertool/validate-me.png new file mode 100644 index 00000000000..cab103100b7 Binary files /dev/null and b/docs/static/teachertool/validate-me.png differ diff --git a/docs/static/teachertool/view-splitter.png b/docs/static/teachertool/view-splitter.png new file mode 100644 index 00000000000..77c09e5bf37 Binary files /dev/null and b/docs/static/teachertool/view-splitter.png differ diff --git a/docs/static/types/sound/effect-tremolo.png b/docs/static/types/sound/effect-tremolo.png new file mode 100644 index 00000000000..4905a09e68d Binary files /dev/null and b/docs/static/types/sound/effect-tremolo.png differ diff --git a/docs/static/types/sound/effect-vibrato.png b/docs/static/types/sound/effect-vibrato.png new file mode 100644 index 00000000000..790e8fd0791 Binary files /dev/null and b/docs/static/types/sound/effect-vibrato.png differ diff --git a/docs/static/types/sound/effect-warble.png b/docs/static/types/sound/effect-warble.png new file mode 100644 index 00000000000..d7221f0ca51 Binary files /dev/null and b/docs/static/types/sound/effect-warble.png differ diff --git a/docs/static/types/sound/freq-hilo.png b/docs/static/types/sound/freq-hilo.png new file mode 100644 index 00000000000..d0719bd043f Binary files /dev/null and b/docs/static/types/sound/freq-hilo.png differ diff --git a/docs/static/types/sound/freq-lohi.png b/docs/static/types/sound/freq-lohi.png new file mode 100644 index 00000000000..465156e50c6 Binary files /dev/null and b/docs/static/types/sound/freq-lohi.png differ diff --git a/docs/static/types/sound/interp-curve.png b/docs/static/types/sound/interp-curve.png new file mode 100644 index 00000000000..db5b2f4f476 Binary files /dev/null and b/docs/static/types/sound/interp-curve.png differ diff --git a/docs/static/types/sound/interp-linear.png b/docs/static/types/sound/interp-linear.png new file mode 100644 index 00000000000..abdf0c52f5e Binary files /dev/null and b/docs/static/types/sound/interp-linear.png differ diff --git a/docs/static/types/sound/interp-log.png b/docs/static/types/sound/interp-log.png new file mode 100644 index 00000000000..a490143d0d7 Binary files /dev/null and b/docs/static/types/sound/interp-log.png differ diff --git a/docs/static/types/sound/noise-wave.png b/docs/static/types/sound/noise-wave.png new file mode 100644 index 00000000000..f5a468e98f1 Binary files /dev/null and b/docs/static/types/sound/noise-wave.png differ diff --git a/docs/static/types/sound/sawtooth-wave.png b/docs/static/types/sound/sawtooth-wave.png new file mode 100644 index 00000000000..3114d801ff9 Binary files /dev/null and b/docs/static/types/sound/sawtooth-wave.png differ diff --git a/docs/static/types/sound/sine-wave.png b/docs/static/types/sound/sine-wave.png new file mode 100644 index 00000000000..6c159b439f8 Binary files /dev/null and b/docs/static/types/sound/sine-wave.png differ diff --git a/docs/static/types/sound/sound-editor.png b/docs/static/types/sound/sound-editor.png new file mode 100644 index 00000000000..a79a651ade9 Binary files /dev/null and b/docs/static/types/sound/sound-editor.png differ diff --git a/docs/static/types/sound/square-wave.png b/docs/static/types/sound/square-wave.png new file mode 100644 index 00000000000..74dcca252b0 Binary files /dev/null and b/docs/static/types/sound/square-wave.png differ diff --git a/docs/static/types/sound/triangle-wave.png b/docs/static/types/sound/triangle-wave.png new file mode 100644 index 00000000000..c75ddbc0ef8 Binary files /dev/null and b/docs/static/types/sound/triangle-wave.png differ diff --git a/docs/static/types/sound/volume-constant.png b/docs/static/types/sound/volume-constant.png new file mode 100644 index 00000000000..bf6b70d8b9c Binary files /dev/null and b/docs/static/types/sound/volume-constant.png differ diff --git a/docs/static/types/sound/volume-hilo.png b/docs/static/types/sound/volume-hilo.png new file mode 100644 index 00000000000..d1dc788ee3b Binary files /dev/null and b/docs/static/types/sound/volume-hilo.png differ diff --git a/docs/static/types/sound/volume-lohi.png b/docs/static/types/sound/volume-lohi.png new file mode 100644 index 00000000000..f80be6802d4 Binary files /dev/null and b/docs/static/types/sound/volume-lohi.png differ diff --git a/docs/static/winapp.PNG b/docs/static/winapp.PNG new file mode 100644 index 00000000000..418c9c299ed Binary files /dev/null and b/docs/static/winapp.PNG differ diff --git a/docs/teachertool.md b/docs/teachertool.md new file mode 100644 index 00000000000..bab897c7b0d --- /dev/null +++ b/docs/teachertool.md @@ -0,0 +1,184 @@ +# Code Evaluation Tool + +## Overview + +The [Code Evaluation Tool]( https://microbit.makecode.com/--eval) is a mechanism for constructing a checklist of requirements for an assignment and running that list automatically against projects in quick succession. This allows teachers to build a checklist, then easily evaluate any number of projects based on that checklist. Projects are evaluated one at a time, but with auto-run enabled, you can update the loaded project by providing a new share link, at which point the rules will automatically be re-run on the new project. + +## Code Evaluation Tool Features + +### Creating, Editing, and Running a Checklist + +#### 1. Creating a new checklist + +Create a new checklist using the **New Checklist** card. If there is already an "in progress" checklist, a warning will appear asking if it is okay to overwrite it. + +![New Checklist](/static/teachertool/new-rubric.png) + +![New Checklist from menu](/static/teachertool/new-rubric-from-menu.png) + +#### 2. Naming a checklist + +The checklist is given a name. + +![Checklist name](/static/teachertool/checklist-name.png) + +#### 3. Add Criteria + +One or more **_criteria_** are added from the catalog using the **Add Criteria** button. + +![Add Criteria](/static/teachertool/add-criteria.png) + +Some criteria (like `[block] used [count] times`) can be added multiple times, others (like `Read a GPIO pin` can only be added once). + +![Criteria items](/static/teachertool/criteria-items.png) + +#### 4. Fill in Parameters + +Parameters for the criteria item are filled in for a criteria item. + +### ~ tip + +#### Parameter types + +From a technical perspective, criteria parameters have these types: + +- **Numeric** parameters have a small input and only allow number inputs. +- **String** parameters can have medium and long sized inputs. +- **Block** parameters should open a block-picker modal. +- **Empty** parameters appear in an error state until they have values. + +### ~ + +Here a block is selected and used 3 times: + +![Criteria parameters 1](/static/teachertool/parameters-1.png) + +Parameter options are displayed and then selected. + +![Criteria parameters 2](/static/teachertool/parameters-2.png) + +![Criteria parameters 3](/static/teachertool/parameters-3.png) + +#### 5. Ask AI + +You can also have an **Ask AI** question as a criteria item in the checklist. You are limited to up to 5 Ask AI questions per checklist. + +![Ask AI criteria](/static/teachertool/ask-ai-criteria.png) + +#### 6. Remove Criteria + +A criteria item is removed using the **trash** button. + +![Remove Criteria](/static/teachertool/remove-criteria.png) + +#### 7. Load a project + +Load a project into the project view by pasting in a share link or share ID. + +![A loaded project](/static/teachertool/loaded-project.png) + +The project will load in read-only mode with the project title appearing at the top of the project view. + +![Project validation](/static/teachertool/validate-me.png) + +#### 8. Run the checklist + +With a project loaded, the checklist can run. The results are shown after clicking the **Run** button. + +![Run checklist](/static/teachertool/run-checklist-button.png) + +The results view lists each criteria with its outcome. + +![Checklist execution](/static/teachertool/checklist-execution.png) + +**Note**: the **Run** Button is disabled without loaded project. + +### Editing Results + +#### 1. Add feedback and notes + +Feedback and notes are added using the **Add Notes** button. The feedback box should resize to fit its content as notes are added. The original feedback remains even if you re-run the rules using the **Run** button. + +![Editing results](/static/teachertool/editing-results-1.png) + +![Editing results](/static/teachertool/editing-results-2.png) + +![Editing results](/static/teachertool/editing-results-3.png) + +#### 2. Edit outcomes + +An outcome is edited using the provided dropdown. + +![Edit outcome](/static/teachertool/edit-outcome-1.png) + +The new selected outcome. + +![Edit outcome](/static/teachertool/edit-outcome-2.png) + +### Result Clearing and Auto-Run + +#### 1. Toggling Auto-run + +Auto-run is toggled either **on** or **off** using the button in the menu. + +![Auto-run button](/static/teachertool/autorun-button.png) + +#### 2. Auto-run disabled + +If auto-run is **disabled**, a result's outcome (i.e. "Looks good", "Needs work", etc...) is set to "Not started" automatically if any of any of the following conditions are met: + +- It is newly added (defaults to the "Not Started" state). +- A parameter in a rule is changed (only the affected rule enters the "Not Started" state). +- The loaded project changes (all rules are be set to "Not started"). + +#### 3. Auto-run enabled + +If auto-run is **enabled**, any rules that enter the "Not started" state due to the conditions listed above are immediately and automatically re-run with their results updated. + +### Loading/Importing/Exporting Checklists + +#### 1. Pre-built checklists + +There are pre-built checklists are available on the welcome page. If a selected checklist is already in-progress, an overwrite confirmation prompt is given. + +![Pre-built checklists](/static/teachertool/prebuilt-rubrics.png) + +#### 2. Export a checklist + +A checklist is exported using the vertical "..." menu near the "auto-run" button. Only the checklist is exported, not the results. For a copy of the results, use the [print](#other) function in the Results view. + +![Export checklist](/static/teachertool/export-checklist.png) + +This will download a json file for the checklist. + +![Checklist download](/static/teachertool/checklist-download.png) + +#### 3. Import a checklist + +User can import a checklist from a file using the same "..." menu, or from the card on the welcome page. + +![Import checklist card](/static/teachertool/import-checklist-card.png) + +![Import checklist menu](/static/teachertool/import-checklist-menu.png) + +Checklist file is selected using "Browse" or dropped directly into the popup. An overwrite confirmation prompted if there is currently an in-progress checklist. + +![Import checklist drag in](/static/teachertool/import-checklist-dragdrop-1.png) + +![Import checklist drop off](/static/teachertool/import-checklist-dragdrop-2.png) + +### Other + +If the page is refreshed (or if the browser closes/re-opens), the current checklist preserved. + +Use the print button to create a version of the results with the outcomes and feedback visible (the other UI elements are hidden). + +![Print button](/static/teachertool/print-button.png) + +The checklist-view/project-view splitter can be resized. It can also be reset to 50/50 split with double-click. + +![View splitter button](/static/teachertool/view-splitter.png) + +Slide the splitter to widen the view of the criteria and results. + +![Split view resize](/static/teachertool/split-resize.png) diff --git a/docs/teachertool/ai-faq.md b/docs/teachertool/ai-faq.md new file mode 100644 index 00000000000..e44625c92b3 --- /dev/null +++ b/docs/teachertool/ai-faq.md @@ -0,0 +1,31 @@ +# Microsoft MakeCode Code Evaluation Tool + +## Responsible AI FAQ + +### 1. What is the MakeCode Code Evaluation Tool? + +The MakeCode Code Evaluation tool is an online tool for teachers to help them understand and evaluate student block-based coding programs. In addition to static analysis functionality, there is an optional AI component for teachers to provide additional feedback and recommendations to students. The teacher can ask specific questions about one student project at a time (i.e. "Do the variables in this program have meaningful names?"), and the AI will respond with an answer and reasoning. + +### 2. What can the MakeCode Code Evaluation Tool do? + +The MakeCode Code Evaluation tool will send the current student code with the teacher question to DeepPrompt (a Microsoft Azure LLM service) along with some contextual prompt information and return the resulting AI response back to the user. + +### 3. What is MakeCode Code Evaluation Tool’s intended use(s)? + +The MakeCode Code Evaluation tool is intended to help teachers expedite the process of giving feedback on student programs. + +### 4. How was the MakeCode Code Evaluation Tool evaluated? What metrics are used to measure performance? + +The system was evaluated with 1000+ prompts from multiple sources to ensure the responses are grounded and relevant to the educator’s task of assessing student code. We evaluated accuracy with red teaming and expert review of responses. + +### 5. What are the limitations of the MakeCode Code Evaluation Tool? How can users minimize the impact of the Code Evaluation Tool’s limitations when using the system? + +The system only supports educational scenarios related to student code. The system will not perform well for other scenarios or unrelated questions. When using this tool, educators should ask short, concise questions relating to the assessment of student code. Questions are limited to 5 per program, and 1000 characters per question. The MakeCode Code Evaluation tool cannot provide direct scores or grades for student work. + +### ~reminder + +#### Tool Beta + +This tool is currently in Beta, and we value your feedback. Please click on the **Feedback** button to share your experiences and thoughts about the MakeCode Code Evaluation Tool. + +### ~ diff --git a/docs/teachertool/carousels/checklists-for-games/cards.json b/docs/teachertool/carousels/checklists-for-games/cards.json new file mode 100644 index 00000000000..96134f4e3f6 --- /dev/null +++ b/docs/teachertool/carousels/checklists-for-games/cards.json @@ -0,0 +1,66 @@ +{ + "cards": [ + { + "cardType": "checklist-resource", + "cardTitle": "Rock Paper Scissors", + "imageUrl": "/static/mb/projects/a4-motion.png", + "checklistUrl": "/teachertool/checklists/rock-paper-scissors.json" + }, + { + "cardType": "checklist-resource", + "cardTitle": "Coin Flipper", + "imageUrl": "/static/mb/projects/coin-flipper.png", + "checklistUrl": "/teachertool/checklists/coin-flipper.json" + }, + { + "cardType": "checklist-resource", + "cardTitle": "7 seconds", + "imageUrl": "/static/mb/projects/7-seconds.png", + "checklistUrl": "/teachertool/checklists/7-seconds.json" + }, + { + "cardType": "checklist-resource", + "cardTitle": "Hot Potato", + "imageUrl": "/static/mb/projects/hot-potato.png", + "checklistUrl": "/teachertool/checklists/hot-potato.json" + }, + { + "cardType": "checklist-resource", + "cardTitle": "Tug-Of-LED", + "imageUrl": "/static/mb/projects/tug-of-led.png", + "checklistUrl": "/teachertool/checklists/tug-of-led.json" + }, + { + "cardType": "checklist-resource", + "cardTitle": "Snap the dot", + "imageUrl": "/static/mb/projects/snap-the-dot.png", + "checklistUrl": "/teachertool/checklists/snap-the-dot.json" + } + ], + "cards.hidden": [ + { + "cardType": "checklist-resource", + "cardTitle": "Rock Paper Scissors V2", + "imageUrl": "/static/mb/projects/a4-motion-v2.png", + "checklistUrl": "/teachertool/checklists/rock-paper-scissors-mbv2.json" + }, + { + "cardType": "checklist-resource", + "cardTitle": "Heads Guess!", + "imageUrl": "/static/mb/projects/heads-guess.png", + "checklistUrl": "/teachertool/checklists/heads-guess.json" + }, + { + "cardType": "checklist-resource", + "cardTitle": "Reaction Time", + "imageUrl": "/static/mb/projects/reaction.jpg", + "checklistUrl": "/teachertool/checklists/reaction-time.json" + }, + { + "cardType": "checklist-resource", + "cardTitle": "Magic Button Trick", + "imageUrl": "/static/mb/projects/magic-button-trick.png", + "checklistUrl": "/teachertool/checklists/magic-button-trick.json" + } + ] +} diff --git a/docs/teachertool/carousels/checklists-for-tools/cards.json b/docs/teachertool/carousels/checklists-for-tools/cards.json new file mode 100644 index 00000000000..4f2c2d19a3c --- /dev/null +++ b/docs/teachertool/carousels/checklists-for-tools/cards.json @@ -0,0 +1,11 @@ +{ + "cards": [ + { + "cardType": "checklist-resource", + "cardTitle": "Level", + "imageUrl": "/static/mb/projects/level.png", + "checklistUrl": "/teachertool/checklists/level.json" + } + ], + "cards.hidden": [] +} diff --git a/docs/teachertool/carousels/checklists-for-tutorials-v2/cards.json b/docs/teachertool/carousels/checklists-for-tutorials-v2/cards.json new file mode 100644 index 00000000000..4dab92f3527 --- /dev/null +++ b/docs/teachertool/carousels/checklists-for-tutorials-v2/cards.json @@ -0,0 +1,41 @@ +{ + "cards": [ + { + "cardType": "checklist-resource", + "cardTitle": "Pet Hamster", + "imageUrl": "/static/mb/projects/pet-hamster.png", + "checklistUrl": "/teachertool/checklists/pet-hamster-mbv2.json" + }, + { + "cardType": "checklist-resource", + "cardTitle": "Countdown", + "imageUrl": "/static/mb/projects/countdown.png", + "checklistUrl": "/teachertool/checklists/countdown-mbv2.json" + }, + { + "cardType": "checklist-resource", + "cardTitle": "Morse Chat", + "imageUrl": "/static/mb/projects/morse-chat.png", + "checklistUrl": "/teachertool/checklists/morse-chat-mbv2.json" + }, + { + "cardType": "checklist-resource", + "cardTitle": "Clap Lights", + "imageUrl": "/static/mb/projects/clap-lights.png", + "checklistUrl": "/teachertool/checklists/clap-lights-mbv2.json" + }, + { + "cardType": "checklist-resource", + "cardTitle": "Blow Away", + "imageUrl": "/static/mb/projects/blow-away.png", + "checklistUrl": "/teachertool/checklists/blow-away-mbv2.json" + }, + { + "cardType": "checklist-resource", + "cardTitle": "Cat Napping", + "imageUrl": "/static/mb/projects/cat-napping/1_lychee.png", + "checklistUrl": "/teachertool/checklists/cat-napping-mbv2.json" + } + ], + "cards.hidden": [] +} diff --git a/docs/teachertool/carousels/checklists-for-tutorials/cards.json b/docs/teachertool/carousels/checklists-for-tutorials/cards.json new file mode 100644 index 00000000000..e9a4d25a0be --- /dev/null +++ b/docs/teachertool/carousels/checklists-for-tutorials/cards.json @@ -0,0 +1,41 @@ +{ + "cards": [ + { + "cardType": "checklist-resource", + "cardTitle": "Flashing Heart", + "imageUrl": "/static/mb/projects/a1-display.png", + "checklistUrl": "/teachertool/checklists/flashing-heart.json" + }, + { + "cardType": "checklist-resource", + "cardTitle": "Name Tag", + "imageUrl": "/static/mb/projects/name-tag.png", + "checklistUrl": "/teachertool/checklists/name-tag.json" + }, + { + "cardType": "checklist-resource", + "cardTitle": "Smiley Buttons", + "imageUrl": "/static/mb/projects/a2-buttons.png", + "checklistUrl": "/teachertool/checklists/smiley-buttons.json" + }, + { + "cardType": "checklist-resource", + "cardTitle": "Dice", + "imageUrl": "/static/mb/projects/dice.png", + "checklistUrl": "/teachertool/checklists/dice.json" + }, + { + "cardType": "checklist-resource", + "cardTitle": "Love Meter", + "imageUrl": "/static/mb/projects/a3-pins.png", + "checklistUrl": "/teachertool/checklists/love-meter.json" + }, + { + "cardType": "checklist-resource", + "cardTitle": "Micro Chat", + "imageUrl": "/static/mb/projects/a9-radio.png", + "checklistUrl": "/teachertool/checklists/micro-chat.json" + } + ], + "cards.hidden": [] +} diff --git a/docs/teachertool/catalog.json b/docs/teachertool/catalog.json new file mode 100644 index 00000000000..0841326309c --- /dev/null +++ b/docs/teachertool/catalog.json @@ -0,0 +1,226 @@ +{ + "criteria": [ + { + "id": "35610CA0-38F8-4CCE-BAB9-99593DB3358A", + "use": "responds_to_events", + "template": "Responds to at least ${count} different event(s)", + "description": "At least the specified number of event blocks are present.", + "docPath": "/teachertool", + "maxCount": 1, + "tags": ["Input and Output"], + "params": [ + { + "name": "count", + "type": "number", + "default": 1, + "paths": ["checks[0].count"] + } + ] + }, + { + "id": "3F7A9DB3-0B5E-456B-86E7-79573F9F6E53", + "use": "uses_input", + "template": "Uses input", + "description": "At least one block that reads or reacts to user input is present.", + "docPath": "/teachertool", + "maxCount": 1, + "tags": ["Input and Output"] + }, + { + "id": "D285D79B-85E5-4C8D-82D2-5A9E35AB1163", + "use": "has_output", + "template": "Produces output", + "description": "At least one block that lights up LEDs, makes sound, or writes to pins is present.", + "docPath": "/teachertool", + "maxCount": 1, + "tags": ["Input and Output"] + }, + { + "id": "2CA4A5DA-4690-4514-97F5-2FE145AB3A59", + "use": "uses_led_coordinates", + "template": "Uses LED coordinates at least ${count} time(s)", + "description": "Uses blocks with LED coordinate inputs at least the specified number of times.", + "docPath": "/teachertool", + "maxCount": 1, + "tags": ["Code Elements"], + "params": [ + { + "name": "count", + "type": "number", + "default": 1, + "paths": ["checks[0].count"] + } + ] + }, + { + "id": "BBB47818-B35F-404C-89A1-D6A594CE9E30", + "use": "sends_radio_message", + "template": "Sends radio messages", + "description": "Radio group is set and at least one block that sends a radio message is present.", + "docPath": "/teachertool", + "maxCount": 1, + "tags": ["Input and Output"] + }, + { + "id": "8C8792C4-31C4-439D-ACAB-C99C9B8250AD", + "use": "receives_radio_message", + "template": "Receives radio messages", + "description": "Radio group is set and at least one block that listens for radio messages is present.", + "docPath": "/teachertool", + "maxCount": 1, + "tags": ["Input and Output"] + }, + { + "id": "18C44CC4-497F-45EC-90FA-CE7DB117AD03", + "use": "set_radio_group_on_start", + "template": "Sets the radio group on startup", + "description": "The 'radio set group' block is called in the 'on start' event.", + "hideInCatalog": true, + "docPath": "/teachertool" + }, + { + "id": "7ECB3AD5-F1C1-4106-9259-802B2E69A7A2", + "use": "send_radio_string_on_button_press", + "template": "Sends a radio string when a button is pressed", + "description": "The 'radio send string' block is called inside a button press event", + "hideInCatalog": true, + "docPath": "/teachertool" + }, + { + "id": "FAA97F77-C9F5-4D58-A3D5-47F965F4B6E2", + "use": "any_on_radio_received", + "template": "Listens for incoming radio messages", + "description": "Any 'on radio received' event (string, number, or name + value) is present.", + "hideInCatalog": true, + "docPath": "/teachertool" + }, + { + "id": "D249AB3E-2620-4E33-911E-284303455365", + "use": "on_radio_received_and_displayed", + "template": "Displays the received radio message on the screen", + "description": "A 'show' block is called with the received message inside the 'on radio received' event.", + "hideInCatalog": true, + "docPath": "/teachertool" + }, + { + "id": "7C2F70AB-2A00-4E35-8227-E5756957D7B3", + "use": "on_shake_gesture", + "template": "Runs code when the micro:bit is shaken", + "description": "When the user shakes the micro:bit, the code inside this block will run.", + "hideInCatalog": true, + "docPath": "/teachertool" + }, + { + "id": "7CFD9718-E841-4286-9563-B70D2F22D8D8", + "use": "variable_declared_called_hand", + "template": "Declares a variable called 'hand'", + "description": "The project includes a variable called 'hand'", + "hideInCatalog": true, + "docPath": "/teachertool" + }, + { + "id": "499EEFAB-2487-427E-8081-28EE031C7D17", + "use": "hand_equal_to_number", + "template": "Checks the value of the variable 'hand'", + "description": "The project checks the value of the variable 'hand'", + "hideInCatalog": true, + "docPath": "/teachertool" + }, + { + "id": "FD7E03B7-53F2-41B9-93ED-51AEA864468E", + "use": "conditional_show_icon", + "template": "Show an icon on display when a condition is met", + "description": "The project shows an icon on the display when a condition is met", + "hideInCatalog": true, + "docPath": "/teachertool" + }, + { + "id": "850DBBDE-71BA-48D2-A1E4-7AC19716C976", + "use": "get_sound_level", + "template": "Sound level is detected in the program", + "hideInCatalog": true, + "docPath": "/teachertool" + }, + { + "id": "F82F6BB4-2B1C-4CFF-92BA-65CE63DD6399", + "use": "soundlevel_greater_than_check", + "template": "Check that the sound level is greater than some number", + "hideInCatalog": true, + "docPath": "/teachertool" + }, + { + "id": "B835FAA0-8CA6-4E4A-95B4-7FC6D15231BD", + "use": "soundlevel_gt_condition", + "template": "If a detected sound level is greater than some number, then two variables are set to random values", + "description": "An if statement checks a detected sound level is greater than a predetermined number. If this is true, variables 'row' and 'col' are set to random values", + "hideInCatalog": true, + "docPath": "/teachertool" + }, + { + "id": "CCA30B9D-4ED7-4916-94AF-050FC473FAA1", + "use": "col_variable_set_random", + "template": "Variable named 'col' is set to a random value", + "hideInCatalog": true, + "docPath": "/teachertool" + }, + { + "id": "162222FB-8E2B-4131-9EA3-EC6B83DDAFB2", + "use": "row_variable_set_random", + "template": "Variable named 'row' is set to a random value", + "hideInCatalog": true, + "docPath": "/teachertool" + }, + { + "id": "01A65046-D4BE-44B6-8273-7A878A71B3D0", + "use": "point_bool_check", + "template": "Check that an LED is lit on the screen", + "description": "At the spot ('col', 'row') on the LED screen, the LED is on", + "hideInCatalog": true, + "docPath": "/teachertool" + }, + { + "id": "E3286B6D-5BEB-43EC-B246-C956AAC34C3E", + "use": "unplot_vars_used", + "template": "Turn off an LED at point ('col', 'row')", + "hideInCatalog": true, + "docPath": "/teachertool" + }, + { + "id": "46AFEDA3-35CB-4041-BEC4-53C0D29E57BA", + "use": "col_add_num", + "template": "Sum the value of the 'col' variable and a given number", + "hideInCatalog": true, + "docPath": "/teachertool" + }, + { + "id": "6BA59761-9464-4C8D-9D8F-E8985273FB92", + "use": "plot_vars_used", + "template": "Turn on an LED at point ('col' + 1, 'row')", + "hideInCatalog": true, + "docPath": "/teachertool" + }, + { + "id": "5934E4C6-7AE4-46A4-8F92-99101987D064", + "use": "show_icon_on_start", + "template": "Show an icon on the LED screen when the program starts", + "description": "An icon block is used inside the on start block", + "hideInCatalog": true, + "docPath": "/teachertool" + }, + { + "id": "0B2BC680-D79D-42C8-BA13-B6B189E2BF7D", + "use": "point_condition", + "template": "If an LED is on, turn it off and light up a different LED", + "hideInCatalog": true, + "docPath": "/teachertool" + }, + { + "id": "ECC79C65-56DC-44A9-98FC-147F5EED87CC", + "use": "blow_away_completeness", + "template": "Project completeness", + "description": "The project contains all the blocks required at the end of the tutorial", + "hideInCatalog": true, + "docPath": "/teachertool" + } + ] +} diff --git a/docs/teachertool/checklists/7-seconds.json b/docs/teachertool/checklists/7-seconds.json new file mode 100644 index 00000000000..019a1d04b68 --- /dev/null +++ b/docs/teachertool/checklists/7-seconds.json @@ -0,0 +1,80 @@ +{ + "name": "7 Seconds", + "criteria": [ + { + "catalogCriteriaId": "499F3572-E655-4DEE-953B-5F26BF0191D7", + "instanceId": "a7QWrsWn0sOe8qIZEtK8p", + "params": [ + { + "name": "question", + "value": "Does this create a game that checks the amount of time between user inputs?" + }, + { + "name": "shareid" + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "4OPG8aTODxTYqdIp9CvVe", + "params": [ + { + "name": "block", + "value": "device_show_number" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "35610CA0-38F8-4CCE-BAB9-99593DB3358A", + "instanceId": "ulOP1FcvkDiT7fPZo98Ep", + "params": [ + { + "name": "count", + "value": "2" + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "OaBYXlO_DLTCGQrf6j7wO", + "params": [ + { + "name": "block", + "value": "math_arithmetic" + }, + { + "name": "count", + "value": "2" + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "9TEFyOGOqliVZ-QgO1pUr", + "params": [ + { + "name": "block", + "value": "math_op3" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "0DFA44C8-3CA5-4C77-946E-AF09F6C03879", + "instanceId": "FPSLwAHCNnnzPPyxRMbtP", + "params": [ + { + "name": "count", + "value": "3" + } + ] + } + ] +} diff --git a/docs/teachertool/checklists/blow-away-mbv2.json b/docs/teachertool/checklists/blow-away-mbv2.json new file mode 100644 index 00000000000..479b0697ef2 --- /dev/null +++ b/docs/teachertool/checklists/blow-away-mbv2.json @@ -0,0 +1,112 @@ +{ + "name": "Blow Away", + "criteria": [ + { + "catalogCriteriaId": "499F3572-E655-4DEE-953B-5F26BF0191D7", + "instanceId": "FjxF6KNgCdTZtf2vOVF8P", + "params": [ + { + "name": "question", + "value": "Does this illustrate an understanding of how to combine loops, variables, and conditionals to react to specific input?" + }, + { + "name": "shareid" + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "Hmf83iLtMk6WMeS1zudeU", + "params": [ + { + "name": "block", + "value": "controls_if" + }, + { + "name": "count", + "value": "2" + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "uuvrA6XVfqwhkf1gHWu9n", + "params": [ + { + "name": "block", + "value": "device_plot" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "FZbp2yqQ1SqEUzDhea8_w", + "params": [ + { + "name": "block", + "value": "device_unplot" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "0DFA44C8-3CA5-4C77-946E-AF09F6C03879", + "instanceId": "q17cW-cZn2mUApFrzsc06", + "params": [ + { + "name": "count", + "value": "2" + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "ZLZLk3aHNafaf2ug3sVTv", + "params": [ + { + "name": "block", + "value": "device_random" + }, + { + "name": "count", + "value": "2" + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "MslaOemw2r7fVuuLBDtfw", + "params": [ + { + "name": "block", + "value": "basic_show_icon" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "ws49proTisn9q-4RXhSsC", + "params": [ + { + "name": "block", + "value": "controls_repeat_ext" + }, + { + "name": "count", + "value": 1 + } + ] + } + ] +} diff --git a/docs/teachertool/checklists/cat-napping-mbv2.json b/docs/teachertool/checklists/cat-napping-mbv2.json new file mode 100644 index 00000000000..a5fcd678c52 --- /dev/null +++ b/docs/teachertool/checklists/cat-napping-mbv2.json @@ -0,0 +1,126 @@ +{ + "name": "Cat Napping", + "criteria": [ + { + "catalogCriteriaId": "499F3572-E655-4DEE-953B-5F26BF0191D7", + "instanceId": "Rl-dj7lg_xpXu1RhIYVmS", + "params": [ + { + "name": "question", + "value": "Does this illustrate an understanding of the learning objective: log data from the micro:bit sensors?" + }, + { + "name": "shareid" + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "f8wk_2AIow7QnThPQqInk", + "params": [ + { + "name": "block", + "value": "dataloggerlog" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "o6lsV31zLKrhiTs7Ag6Cq", + "params": [ + { + "name": "block", + "value": "controls_if" + }, + { + "name": "count", + "value": "2" + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "_V7WDtvTn0Le-Im2mUft5", + "params": [ + { + "name": "block", + "value": "logic_negate" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "0DFA44C8-3CA5-4C77-946E-AF09F6C03879", + "instanceId": "33lYqeqO_RzCa2M1zsXUU", + "params": [ + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "35610CA0-38F8-4CCE-BAB9-99593DB3358A", + "instanceId": "f0E6ROHk34S5abRMr94Wk", + "params": [ + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "D285D79B-85E5-4C8D-82D2-5A9E35AB1163", + "instanceId": "ounrfFdAj8axhTVJv1nZs" + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "js4ICKNVYB2EX1NPk6xO1", + "params": [ + { + "name": "block", + "value": "device_get_light_level" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "wPL95RYND2ItmrhCqTGUA", + "params": [ + { + "name": "block", + "value": "device_temperature" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "-Hn00i7U2e1mp7_7tZxmg", + "params": [ + { + "name": "block", + "value": "every_interval" + }, + { + "name": "count", + "value": 1 + } + ] + } + ] +} diff --git a/docs/teachertool/checklists/clap-lights-mbv2.json b/docs/teachertool/checklists/clap-lights-mbv2.json new file mode 100644 index 00000000000..3353b7e9f11 --- /dev/null +++ b/docs/teachertool/checklists/clap-lights-mbv2.json @@ -0,0 +1,120 @@ +{ + "name": "Clap Lights", + "criteria": [ + { + "catalogCriteriaId": "499F3572-E655-4DEE-953B-5F26BF0191D7", + "instanceId": "6Li5DrIKNCdYyThrnaNn-", + "params": [ + { + "name": "question", + "value": "Does this illustrate mastery of conditionals and variables?" + }, + { + "name": "shareid" + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "i70kfW9Edf_w4uWQOr4Vj", + "params": [ + { + "name": "block", + "value": "pxt-on-start" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "opfcvo0hPmgZYncOHd-gm", + "params": [ + { + "name": "block", + "value": "input_on_sound" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "3F7A9DB3-0B5E-456B-86E7-79573F9F6E53", + "instanceId": "vWaXdWe0VMs1bKIcaypf2" + }, + { + "catalogCriteriaId": "D285D79B-85E5-4C8D-82D2-5A9E35AB1163", + "instanceId": "12aPxkq3Yef9RswkE98w5" + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "wqVmzKZ9qwQHHbPjQ1m7W", + "params": [ + { + "name": "block", + "value": "controls_if" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "De13zLjU1qYqav2lMEVPr", + "params": [ + { + "name": "block", + "value": "device_show_leds" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "YKWUiU8ya18wEGqtrkgB6", + "params": [ + { + "name": "block", + "value": "device_clear_display" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "0DFA44C8-3CA5-4C77-946E-AF09F6C03879", + "instanceId": "X4flMR_NXpX3_m7Rv8NgZ", + "params": [ + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "uOoaqb6uAXhGw8w61QUQV", + "params": [ + { + "name": "block", + "value": "logic_negate" + }, + { + "name": "count", + "value": 1 + } + ] + } + ] +} diff --git a/docs/teachertool/checklists/coin-flipper.json b/docs/teachertool/checklists/coin-flipper.json new file mode 100644 index 00000000000..eb7f191cb03 --- /dev/null +++ b/docs/teachertool/checklists/coin-flipper.json @@ -0,0 +1,60 @@ +{ + "name": "Coin Flipper", + "criteria": [ + { + "catalogCriteriaId": "499F3572-E655-4DEE-953B-5F26BF0191D7", + "instanceId": "Z3zTdUo6FvGBDSg_d2hRc", + "params": [ + { + "name": "question", + "value": "Does this illustrate an understanding of how to use conditional statements?" + }, + { + "name": "shareid" + } + ] + }, + { + "catalogCriteriaId": "35610CA0-38F8-4CCE-BAB9-99593DB3358A", + "instanceId": "OJqELTqpWU2p-GiE9SBrD", + "params": [ + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "ea1DcsEklzTyYriLiTw5h", + "params": [ + { + "name": "block", + "value": "controls_if" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "4ipvY3gRE6eQ0ygLizvIA", + "params": [ + { + "name": "block", + "value": "logic_random" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "D285D79B-85E5-4C8D-82D2-5A9E35AB1163", + "instanceId": "s-kiwMcNWOUMIAh3ydEG7" + } + ] +} diff --git a/docs/teachertool/checklists/countdown-mbv2.json b/docs/teachertool/checklists/countdown-mbv2.json new file mode 100644 index 00000000000..1f2fda4ec57 --- /dev/null +++ b/docs/teachertool/checklists/countdown-mbv2.json @@ -0,0 +1,88 @@ +{ + "name": "Countdown", + "criteria": [ + { + "catalogCriteriaId": "499F3572-E655-4DEE-953B-5F26BF0191D7", + "instanceId": "2jWFi2zbqRT4SxUJ7_3la", + "params": [ + { + "name": "question", + "value": "Does this illustrate an understanding of how to use the index in a for loop?" + }, + { + "name": "shareid" + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "QfP2rNybJK3wz-dCkUrRk", + "params": [ + { + "name": "block", + "value": "pxt_controls_for" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "dzj7hJWaibjqvmZ8-OyI2", + "params": [ + { + "name": "block", + "value": "music_tone_playable" + }, + { + "name": "count", + "value": "2" + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "pghLAad8ZFgm7UQGFZ5m9", + "params": [ + { + "name": "block", + "value": "device_show_number" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "upuqZtKii4pmKbRZ62WkC", + "params": [ + { + "name": "block", + "value": "math_arithmetic" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "6YsjtxoGig3BCInRCFToi", + "params": [ + { + "name": "block", + "value": "device_print_message" + }, + { + "name": "count", + "value": 1 + } + ] + } + ] +} diff --git a/docs/teachertool/checklists/dice.json b/docs/teachertool/checklists/dice.json new file mode 100644 index 00000000000..a94ee892535 --- /dev/null +++ b/docs/teachertool/checklists/dice.json @@ -0,0 +1,60 @@ +{ + "name": "Dice", + "criteria": [ + { + "catalogCriteriaId": "499F3572-E655-4DEE-953B-5F26BF0191D7", + "instanceId": "VuYiaGq1XnALp52yvG3i-", + "params": [ + { + "name": "question", + "value": "Does this illustrate an understanding of how to display a random value in reaction to an event?" + }, + { + "name": "shareid" + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "KWW_gxy3yvurF6eN9SrBw", + "params": [ + { + "name": "block", + "value": "device_show_number" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "HmHaX65Lecyjy1Xru9KAM", + "params": [ + { + "name": "block", + "value": "device_random" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "owqvpBn_QYkAJGWvzb_gC", + "params": [ + { + "name": "block", + "value": "device_gesture_event" + }, + { + "name": "count", + "value": 1 + } + ] + } + ] +} diff --git a/docs/teachertool/checklists/flashing-heart.json b/docs/teachertool/checklists/flashing-heart.json new file mode 100644 index 00000000000..7155a7cad75 --- /dev/null +++ b/docs/teachertool/checklists/flashing-heart.json @@ -0,0 +1,46 @@ +{ + "name": "Flashing Heart", + "criteria": [ + { + "catalogCriteriaId": "499F3572-E655-4DEE-953B-5F26BF0191D7", + "instanceId": "VuYiaGq1XnALp52yvG3i-", + "params": [ + { + "name": "question", + "value": "Does this illustrate an understanding of the learning objective: alternate between LED images on the micro:bit display?" + }, + { + "name": "shareid" + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "pE47UT6Uv4jtezvffy3DZ", + "params": [ + { + "name": "block", + "value": "device_forever" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "Qfpv6fPa0BbQNJ2Hfusgx", + "params": [ + { + "name": "block", + "value": "device_show_leds" + }, + { + "name": "count", + "value": "2" + } + ] + } + ] +} diff --git a/docs/teachertool/checklists/heads-guess.json b/docs/teachertool/checklists/heads-guess.json new file mode 100644 index 00000000000..ad57b6aa49a --- /dev/null +++ b/docs/teachertool/checklists/heads-guess.json @@ -0,0 +1,4 @@ +{ + "name": "Heads Guess!", + "criteria": [] +} diff --git a/docs/teachertool/checklists/hot-potato.json b/docs/teachertool/checklists/hot-potato.json new file mode 100644 index 00000000000..44d4a3dbb5d --- /dev/null +++ b/docs/teachertool/checklists/hot-potato.json @@ -0,0 +1,78 @@ +{ + "name": "Hot Potato", + "criteria": [ + { + "catalogCriteriaId": "499F3572-E655-4DEE-953B-5F26BF0191D7", + "instanceId": "fnXRgd5KGQWBS_MnWa1uH", + "params": [ + { + "name": "question", + "value": "Does this illustrate an understanding of how to use while loops?" + }, + { + "name": "shareid" + } + ] + }, + { + "catalogCriteriaId": "3F7A9DB3-0B5E-456B-86E7-79573F9F6E53", + "instanceId": "Q6R68y1CDXRKwOfgxZK3i" + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "xI_nB059k2rtEFbbY6kNf", + "params": [ + { + "name": "block", + "value": "device_while" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "QbJy_9aFD4qLdzBWzfXVG", + "params": [ + { + "name": "block", + "value": "logic_compare" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "0DFA44C8-3CA5-4C77-946E-AF09F6C03879", + "instanceId": "d-YN_K3QPwfDHis7dpWqb", + "params": [ + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "tgwkiJw-1nCJnDZdRmo4M", + "params": [ + { + "name": "block", + "value": "device_pause" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "D285D79B-85E5-4C8D-82D2-5A9E35AB1163", + "instanceId": "JdWIqw1Nd-6-Zpk-wDyT5" + } + ] +} diff --git a/docs/teachertool/checklists/level.json b/docs/teachertool/checklists/level.json new file mode 100644 index 00000000000..fe7165c556b --- /dev/null +++ b/docs/teachertool/checklists/level.json @@ -0,0 +1,60 @@ +{ + "name": "Level", + "criteria": [ + { + "catalogCriteriaId": "499F3572-E655-4DEE-953B-5F26BF0191D7", + "instanceId": "zBovdISYJKVRHRGeI1hiZ", + "params": [ + { + "name": "question", + "value": "Does this illustrate an understanding of how to use conditionals when responding to data from the micro:bit sensors?" + }, + { + "name": "shareid" + } + ] + }, + { + "catalogCriteriaId": "0DFA44C8-3CA5-4C77-946E-AF09F6C03879", + "instanceId": "ZZSKu3-adfTotvajnh_Oq", + "params": [ + { + "name": "count", + "value": "2" + } + ] + }, + { + "catalogCriteriaId": "D285D79B-85E5-4C8D-82D2-5A9E35AB1163", + "instanceId": "h5nxMFMDyip_ILocN9NLF" + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "w6Pqhe1uOUqMUSbtqXsOZ", + "params": [ + { + "name": "block", + "value": "controls_if" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "1dHPqKGOEnofIu22OY3JY", + "params": [ + { + "name": "block", + "value": "logic_compare" + }, + { + "name": "count", + "value": "2" + } + ] + } + ] +} diff --git a/docs/teachertool/checklists/love-meter.json b/docs/teachertool/checklists/love-meter.json new file mode 100644 index 00000000000..121708ecbb7 --- /dev/null +++ b/docs/teachertool/checklists/love-meter.json @@ -0,0 +1,70 @@ +{ + "name": "Love Meter", + "criteria": [ + { + "catalogCriteriaId": "499F3572-E655-4DEE-953B-5F26BF0191D7", + "instanceId": "b6f1ZKD6IyhuZPrrO5yhl", + "params": [ + { + "name": "question", + "value": "Does this illustrate an understanding of how to display a random number in response to input?" + }, + { + "name": "shareid" + } + ] + }, + { + "catalogCriteriaId": "35610CA0-38F8-4CCE-BAB9-99593DB3358A", + "instanceId": "PV1SB_UrMFZAB9lRjsjaY", + "params": [ + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "oCAOEeq9SP43nvSj3y2W-", + "params": [ + { + "name": "block", + "value": "device_pin_event" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "bIIoZSN63Nm_QBIViMkix", + "params": [ + { + "name": "block", + "value": "device_random" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "8vyJcx9RWw__JDgrhRr9F", + "params": [ + { + "name": "block", + "value": "device_show_number" + }, + { + "name": "count", + "value": 1 + } + ] + } + ] +} diff --git a/docs/teachertool/checklists/magic-button-trick.json b/docs/teachertool/checklists/magic-button-trick.json new file mode 100644 index 00000000000..49970603a73 --- /dev/null +++ b/docs/teachertool/checklists/magic-button-trick.json @@ -0,0 +1,4 @@ +{ + "name": "Magic Button Trick", + "criteria": [] +} diff --git a/docs/teachertool/checklists/micro-chat.json b/docs/teachertool/checklists/micro-chat.json new file mode 100644 index 00000000000..f0f4eee63e2 --- /dev/null +++ b/docs/teachertool/checklists/micro-chat.json @@ -0,0 +1,88 @@ +{ + "name": "Micro Chat", + "criteria": [ + { + "catalogCriteriaId": "499F3572-E655-4DEE-953B-5F26BF0191D7", + "instanceId": "ekgIeBQQffGCDYnURb465", + "params": [ + { + "name": "question", + "value": "Does this illustrate an understanding of how to use events and parameters?" + }, + { + "name": "shareid" + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "cFkV7YP3XsbawFHYdLlGY", + "params": [ + { + "name": "block", + "value": "radio_set_group" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "OY1LxTejkMnpahS8a74XJ", + "params": [ + { + "name": "block", + "value": "device_button_event" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "S5bN1NbQUDJG5jbQS3D7t", + "params": [ + { + "name": "block", + "value": "radio_datagram_send_string" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "gUSwg8O8MLqJBw02q0EwV", + "params": [ + { + "name": "block", + "value": "radio_on_string_drag" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "9DzGn5u3E7RxHIFhk-OR9", + "params": [ + { + "name": "block", + "value": "device_print_message" + }, + { + "name": "count", + "value": 1 + } + ] + } + ] +} diff --git a/docs/teachertool/checklists/morse-chat-mbv2.json b/docs/teachertool/checklists/morse-chat-mbv2.json new file mode 100644 index 00000000000..3b36ed6d916 --- /dev/null +++ b/docs/teachertool/checklists/morse-chat-mbv2.json @@ -0,0 +1,123 @@ +{ + "name": "Morse Chat", + "criteria": [ + { + "catalogCriteriaId": "499F3572-E655-4DEE-953B-5F26BF0191D7", + "instanceId": "shqa7cklKhTJlAS8QPH1h", + "params": [ + { + "name": "question", + "value": "Does this produce different outputs based on the radio input that was received?" + }, + { + "name": "shareid" + } + ] + }, + { + "catalogCriteriaId": "499F3572-E655-4DEE-953B-5F26BF0191D7", + "instanceId": "3WfHJ3ZrAfD8pUZgBAOfI", + "params": [ + { + "name": "question", + "value": "Does this send different radio messages in response to different events?" + }, + { + "name": "shareid" + } + ] + }, + { + "catalogCriteriaId": "3F7A9DB3-0B5E-456B-86E7-79573F9F6E53", + "instanceId": "jJn_1FSlxRedkvdJQaRWW" + }, + { + "catalogCriteriaId": "D285D79B-85E5-4C8D-82D2-5A9E35AB1163", + "instanceId": "VetUhdvQtTntHAJY0jGhr" + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "P7oPfgmTihchFDNanx34S", + "params": [ + { + "name": "block", + "value": "radio_datagram_send" + }, + { + "name": "count", + "value": "2" + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "uz1IpTd1IJbQnCYu2EjRI", + "params": [ + { + "name": "block", + "value": "radio_on_number_drag" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "3zUpePzYwDc6bEY0VHiCR", + "params": [ + { + "name": "block", + "value": "controls_if" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "RmNrPc3KzodthijpJiThd", + "params": [ + { + "name": "block", + "value": "logic_compare" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "PPffo8cM0QYcG7aLEwv6R", + "params": [ + { + "name": "block", + "value": "device_show_leds" + }, + { + "name": "count", + "value": "2" + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "DuXY4W3BxwHtiwwcpbkCp", + "params": [ + { + "name": "block", + "value": "music_tone_playable" + }, + { + "name": "count", + "value": "2" + } + ] + } + ] +} diff --git a/docs/teachertool/checklists/name-tag.json b/docs/teachertool/checklists/name-tag.json new file mode 100644 index 00000000000..10ca346e20c --- /dev/null +++ b/docs/teachertool/checklists/name-tag.json @@ -0,0 +1,46 @@ +{ + "name": "Name Tag", + "criteria": [ + { + "catalogCriteriaId": "499F3572-E655-4DEE-953B-5F26BF0191D7", + "instanceId": "dJAm80FLeg53FzhYbHnBx", + "params": [ + { + "name": "question", + "value": "Does this illustrate an understanding of how to display text on the screen?" + }, + { + "name": "shareid" + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "vZ-kFpFxkFRLGFVALg4og", + "params": [ + { + "name": "block", + "value": "device_print_message" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "QG1JsccZotdKi2k1slqyW", + "params": [ + { + "name": "block", + "value": "device_forever" + }, + { + "name": "count", + "value": 1 + } + ] + } + ] +} diff --git a/docs/teachertool/checklists/pet-hamster-mbv2.json b/docs/teachertool/checklists/pet-hamster-mbv2.json new file mode 100644 index 00000000000..9ae37f9ef93 --- /dev/null +++ b/docs/teachertool/checklists/pet-hamster-mbv2.json @@ -0,0 +1,56 @@ +{ + "name": "Pet Hamster", + "criteria": [ + { + "catalogCriteriaId": "499F3572-E655-4DEE-953B-5F26BF0191D7", + "instanceId": "sZv3ich1UjQIXcCIQoKXt", + "params": [ + { + "name": "question", + "value": "Does this illustrate an understanding of the learning objective: show different outputs based on different inputs?" + }, + { + "name": "shareid" + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "1LJH8pJ3hLXQScO7WwRVM", + "params": [ + { + "name": "block", + "value": "basic_show_icon" + }, + { + "name": "count", + "value": "5" + } + ] + }, + { + "catalogCriteriaId": "35610CA0-38F8-4CCE-BAB9-99593DB3358A", + "instanceId": "9yfrg63Vzgm7p4o3tzLRH", + "params": [ + { + "name": "count", + "value": "2" + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "MJiAdJSgb4uEqDHj1SIGL", + "params": [ + { + "name": "block", + "value": "device_builtin_melody_playable" + }, + { + "name": "count", + "value": "2" + } + ] + } + ] +} diff --git a/docs/teachertool/checklists/reaction-time.json b/docs/teachertool/checklists/reaction-time.json new file mode 100644 index 00000000000..32bade3c62c --- /dev/null +++ b/docs/teachertool/checklists/reaction-time.json @@ -0,0 +1,4 @@ +{ + "name": "Reaction Time", + "criteria": [] +} diff --git a/docs/teachertool/checklists/rock-paper-scissors-mbv2.json b/docs/teachertool/checklists/rock-paper-scissors-mbv2.json new file mode 100644 index 00000000000..520806e6a31 --- /dev/null +++ b/docs/teachertool/checklists/rock-paper-scissors-mbv2.json @@ -0,0 +1,4 @@ +{ + "name": "Rock Paper Scissors V2", + "criteria": [] +} diff --git a/docs/teachertool/checklists/rock-paper-scissors.json b/docs/teachertool/checklists/rock-paper-scissors.json new file mode 100644 index 00000000000..26c4ce02c1a --- /dev/null +++ b/docs/teachertool/checklists/rock-paper-scissors.json @@ -0,0 +1,78 @@ +{ + "name": "Rock Paper Scissors", + "criteria": [ + { + "catalogCriteriaId": "499F3572-E655-4DEE-953B-5F26BF0191D7", + "instanceId": "wmJMUZPlga2343TgkC1mq", + "params": [ + { + "name": "question", + "value": "Does this store a random value in a variable and use it in an if, else if, else statement?" + }, + { + "name": "shareid" + } + ] + }, + { + "catalogCriteriaId": "3F7A9DB3-0B5E-456B-86E7-79573F9F6E53", + "instanceId": "rH-U8YN2wFzTf3HWfMT99" + }, + { + "catalogCriteriaId": "D285D79B-85E5-4C8D-82D2-5A9E35AB1163", + "instanceId": "aZKd2jn07vIvBv5ko9LYS" + }, + { + "catalogCriteriaId": "0DFA44C8-3CA5-4C77-946E-AF09F6C03879", + "instanceId": "4K8STmyIcdlJ_euu4dK7i", + "params": [ + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "sIMzMupbeLopUcrF6QfI7", + "params": [ + { + "name": "block", + "value": "device_gesture_event" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "0W_TQ1llic1bxnj3sN-aP", + "params": [ + { + "name": "block", + "value": "controls_if" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "KSblM-323vr3pKCjeJ-Nt", + "params": [ + { + "name": "block", + "value": "device_random" + }, + { + "name": "count", + "value": 1 + } + ] + } + ] +} diff --git a/docs/teachertool/checklists/smiley-buttons.json b/docs/teachertool/checklists/smiley-buttons.json new file mode 100644 index 00000000000..a416c869880 --- /dev/null +++ b/docs/teachertool/checklists/smiley-buttons.json @@ -0,0 +1,42 @@ +{ + "name": "Smiley Buttons", + "criteria": [ + { + "catalogCriteriaId": "499F3572-E655-4DEE-953B-5F26BF0191D7", + "instanceId": "JR66Aahv1mHVPQz6p8bKn", + "params": [ + { + "name": "question", + "value": "Does this produce different outputs based on different events?" + }, + { + "name": "shareid" + } + ] + }, + { + "catalogCriteriaId": "35610CA0-38F8-4CCE-BAB9-99593DB3358A", + "instanceId": "rJ_uRDO6gN8E7Vg33nCfn", + "params": [ + { + "name": "count", + "value": "2" + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "8mQaZHHOweaK3fneSKbhw", + "params": [ + { + "name": "block", + "value": "basic_show_icon" + }, + { + "name": "count", + "value": "2" + } + ] + } + ] +} diff --git a/docs/teachertool/checklists/snap-the-dot.json b/docs/teachertool/checklists/snap-the-dot.json new file mode 100644 index 00000000000..a9b4e6ce796 --- /dev/null +++ b/docs/teachertool/checklists/snap-the-dot.json @@ -0,0 +1,158 @@ +{ + "name": "Snap the Dot", + "criteria": [ + { + "catalogCriteriaId": "499F3572-E655-4DEE-953B-5F26BF0191D7", + "instanceId": "uMK8HfxELgJqLqu6jpUiV", + "params": [ + { + "name": "question", + "value": "Does this illustrate an understanding of comparing numbers in a conditional statement?" + }, + { + "name": "shareid" + } + ] + }, + { + "catalogCriteriaId": "3F7A9DB3-0B5E-456B-86E7-79573F9F6E53", + "instanceId": "_LSvFglEcMuAAz_wDXKlN" + }, + { + "catalogCriteriaId": "0DFA44C8-3CA5-4C77-946E-AF09F6C03879", + "instanceId": "a5YCJSj6FGuPPppjfBwjY", + "params": [ + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "Ojp_6jUfPyz9l7xCdOh12", + "params": [ + { + "name": "block", + "value": "game_create_sprite" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "bFMFlKQJ3bi4SggTfPGQ7", + "params": [ + { + "name": "block", + "value": "game_sprite_bounce" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "qY7jey7BPHj6udRnlejRo", + "params": [ + { + "name": "block", + "value": "game_add_score" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "0XWeRkywzGQiHa58Ci2gb", + "params": [ + { + "name": "block", + "value": "game_move_sprite" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "8PNsPCSOWLh43uVMxSBoT", + "params": [ + { + "name": "block", + "value": "game_game_over" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "sH10T9-q1gMulAz9o5_SW", + "params": [ + { + "name": "block", + "value": "game_sprite_property" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "P-ol0aaMNhSiKywKcV4Dh", + "params": [ + { + "name": "block", + "value": "controls_if" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "l4PVdnJlKTJiPNvfoCDmg", + "params": [ + { + "name": "block", + "value": "logic_compare" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "4Vvt2BYF5jTBKy43Vm0wY", + "params": [ + { + "name": "block", + "value": "device_forever" + }, + { + "name": "count", + "value": 1 + } + ] + } + ] +} diff --git a/docs/teachertool/checklists/tug-of-led.json b/docs/teachertool/checklists/tug-of-led.json new file mode 100644 index 00000000000..7f3cc1af208 --- /dev/null +++ b/docs/teachertool/checklists/tug-of-led.json @@ -0,0 +1,98 @@ +{ + "name": "Tug-Of-LED", + "criteria": [ + { + "catalogCriteriaId": "499F3572-E655-4DEE-953B-5F26BF0191D7", + "instanceId": "mmGgCDQulErVZEr5ha3-J", + "params": [ + { + "name": "question", + "value": "Does this illustrate an understanding of how to increment and decrement a variable based on input?" + }, + { + "name": "shareid" + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "RJkv2Z1UHxIywKbJPn0Fv", + "params": [ + { + "name": "block", + "value": "device_plot" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "ieltnwSKhrOeJzYl1h-wq", + "params": [ + { + "name": "block", + "value": "controls_if" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "E1cUYkJS2TsLxjSwX3Tqi", + "params": [ + { + "name": "block", + "value": "device_print_message" + }, + { + "name": "count", + "value": "2" + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "HfbLPAgcjK4c0aqCdQg7i", + "params": [ + { + "name": "block", + "value": "math_js_round" + }, + { + "name": "count", + "value": 1 + } + ] + }, + { + "catalogCriteriaId": "59AAC5BA-B0B3-4389-AA90-1E767EFA8563", + "instanceId": "r4g2OA8m3z8SVmJBmK-MW", + "params": [ + { + "name": "block", + "value": "device_button_event" + }, + { + "name": "count", + "value": "2" + } + ] + }, + { + "catalogCriteriaId": "0DFA44C8-3CA5-4C77-946E-AF09F6C03879", + "instanceId": "HzOiUmWLHvCLtj9wgOqLA", + "params": [ + { + "name": "count", + "value": 1 + } + ] + } + ] +} diff --git a/docs/teachertool/test/catalog.json b/docs/teachertool/test/catalog.json new file mode 100644 index 00000000000..c5d07890037 --- /dev/null +++ b/docs/teachertool/test/catalog.json @@ -0,0 +1,3 @@ +{ + "criteria": [] +} \ No newline at end of file diff --git a/docs/teachertool/test/validator-plans.json b/docs/teachertool/test/validator-plans.json new file mode 100644 index 00000000000..f0865f8840c --- /dev/null +++ b/docs/teachertool/test/validator-plans.json @@ -0,0 +1,3 @@ +{ + "validatorPlans": [] +} diff --git a/docs/teachertool/validator-plans.json b/docs/teachertool/validator-plans.json new file mode 100644 index 00000000000..48b264bcbeb --- /dev/null +++ b/docs/teachertool/validator-plans.json @@ -0,0 +1,693 @@ +{ + "validatorPlans": [ + { + ".desc": "Set the LED screen.", + "name": "show_icon_on_screen", + "threshold": 1, + "checks": [ + { + "validator": "blocksExist", + "blockCounts": [ + { + "blockId": "basic_show_icon", + "count": 1 + } + ] + } + ] + }, + { + ".desc": "Shows something on the LED screen.", + "name": "has_output", + "threshold": 1, + "checks": [ + { + "validator": "blocksInSetExist", + "blocks": [ + "device_show_number", + "device_show_leds", + "basic_show_icon", + "device_print_message", + "device_plot", + "device_led_toggle", + "device_plot_bar_graph", + "device_plot_brightness", + "device_show_image_offset", + "device_scroll_image", + + "game_create_sprite", + "game_game_over", + "game_add_score", + "game_remove_life", + "game_start_countdown", + + "music_playable_play", + "device_ring", + "music_playable_play_default_bkg", + + "device_set_digital_pin", + "device_set_servo_pin" + ], + "count": 1 + }, + { + "validator": "blocksExist", + "blockCounts": [ + { + "blockId": "game_set_life", + "count": 1 + } + ], + "childValidatorPlans": ["number_zero"] + } + ] + }, + { + ".desc": "Processes input in any form (TODO : Non-Empty Check for event blocks)", + "name": "uses_input", + "threshold": 1, + "checks": [ + { + "validator": "blocksInSetExist", + "blocks": [ + "control_on_event", + "device_button_event", + "device_gesture_event", + "device_pin_event", + "device_pin_released", + "input_logo_event", + "input_on_sound", + "pins_on_pulsed", + "radio_on_number_drag", + "radio_on_string_drag", + "radio_on_value_drag", + "serial_on_data_received", + + "device_acceleration", + "device_get_analog_pin", + "device_get_button2", + "device_get_digital_pin", + "device_get_light_level", + "device_get_magnetic_force", + "device_get_rotation", + "device_get_running_time", + "device_get_running_time_micros", + "device_get_sound_level", + "device_heading", + "device_pin_is_pressed", + "device_temperature", + "deviceisgesture", + "input_logo_is_pressed", + "music_sound_is_playing", + "pins_i2c_readnumber", + "pins_pulse_duration", + "pins_pulse_in", + "serial_read_buffer", + "serial_read_line", + "serial_read_until", + "serial_readbuffer" + ], + "count": 1 + } + ] + }, + { + ".desc": "Runs code in response to any event (TODO : Non-Empty Check)", + "name": "responds_to_events", + "threshold": 1, + "checks": [ + { + "validator": "blocksInSetExist", + "blocks": [ + "control_on_event", + "device_button_event", + "device_gesture_event", + "device_pin_event", + "device_pin_released", + "input_logo_event", + "input_on_sound", + "melody_on_event", + "pins_on_pulsed", + "radio_on_number_drag", + "radio_on_string_drag", + "radio_on_value_drag", + "serial_on_data_received" + ], + "count": 0 + } + ] + }, + { + ".desc": "Checks for blocks that reference LEDs by their coordinates", + "name": "uses_led_coordinates", + "threshold": 1, + "checks": [ + { + "validator": "blocksInSetExist", + "blocks": [ + "device_plot", + "device_led_toggle", + "device_unplot", + "device_point", + "device_plot_brightness", + "device_point_brightness", + "game_create_sprite" + ], + "count": 0 + } + ] + }, + { + ".desc": "Checks that radio group is set and at least one radio send message block is present", + "name": "sends_radio_message", + "threshold": 2, + "checks": [ + { + "validator": "blocksExist", + "blockCounts": [ + { + "blockId": "radio_set_group", + "count": 1 + } + ] + }, + { + "validator": "blocksInSetExist", + "blocks": [ + "radio_datagram_send", + "radio_datagram_send_value", + "radio_datagram_send_string" + ], + "count": 1 + } + ] + }, + { + ".desc": "Checks that radio group is set and at least block that listens for radio messages is present. (TODO : Non-Empty Check)", + "name": "receives_radio_message", + "threshold": 2, + "checks": [ + { + "validator": "blocksExist", + "blockCounts": [ + { + "blockId": "radio_set_group", + "count": 1 + } + ] + }, + { + "validator": "blocksInSetExist", + "blocks": [ + "radio_on_number_drag", + "radio_on_value_drag", + "radio_on_string_drag" + ], + "count": 1 + } + ] + }, + { + ".desc": "Set the radio group.", + "name": "set_radio_group", + "threshold": 1, + "checks": [ + { + "validator": "blocksExist", + "blockCounts": [ + { + "blockId": "radio_set_group", + "count": 1 + } + ] + } + ] + }, + { + ".desc": "Send a string over radio.", + "name": "send_radio_string", + "threshold": 1, + "checks": [ + { + "validator": "blocksExist", + "blockCounts": [ + { + "blockId": "radio_datagram_send_string", + "count": 1 + } + ] + } + ] + }, + { + ".desc": "shows a parameter variable value on the screen", + "name": "show_parameter_value_on_screen", + "threshold": 1, + "checks": [ + { + "validator": "blocksExist", + "blockCounts": [ + { + "blockId": "device_print_message", + "count": 1 + } + ], + "childValidatorPlans": ["parameter_variable_accessed"] + }, + { + "validator": "blocksExist", + "blockCounts": [ + { + "blockId": "device_show_number", + "count": 1 + } + ], + "childValidatorPlans": ["numeric_parameter_variable_accessed"] + } + ] + }, + { + ".desc": "set radio group on start", + "name": "set_radio_group_on_start", + "threshold": 1, + "checks": [ + { + "validator": "blocksExist", + "blockCounts": [ + { + "blockId": "pxt-on-start", + "count": 1 + } + ], + "childValidatorPlans": ["set_radio_group"] + } + ] + }, + { + ".desc": "send radio string in button press event.", + "name": "send_radio_string_on_button_press", + "threshold": 1, + "checks": [ + { + "validator": "blocksExist", + "blockCounts": [ + { + "blockId": "device_button_event", + "count": 1 + } + ], + "childValidatorPlans": ["send_radio_string"] + } + ] + }, + { + ".desc": "one or more of the possible 'on radio received' blocks are present", + "name": "any_on_radio_received", + "threshold": 1, + "checks": [ + { + "validator": "blocksExist", + "blockCounts": [ + { + "blockId": "radio_on_number_drag", + "count": 1 + } + ] + }, + { + "validator": "blocksExist", + "blockCounts": [ + { + "blockId": "radio_on_value_drag", + "count": 1 + } + ] + }, + { + "validator": "blocksExist", + "blockCounts": [ + { + "blockId": "radio_on_string_drag", + "count": 1 + } + ] + } + ] + }, + { + ".desc": "one or more of the possible 'on radio received' blocks are present and display the received content on the screen.", + "name": "on_radio_received_and_displayed", + "threshold": 1, + "checks": [ + { + "validator": "blocksExist", + "blockCounts": [ + { + "blockId": "radio_on_number_drag", + "count": 1 + } + ], + "childValidatorPlans": ["show_parameter_value_on_screen"] + }, + { + "validator": "blocksExist", + "blockCounts": [ + { + "blockId": "radio_on_value_drag", + "count": 1 + } + ], + "childValidatorPlans": ["show_parameter_value_on_screen"] + }, + { + "validator": "blocksExist", + "blockCounts": [ + { + "blockId": "radio_on_string_drag", + "count": 1 + } + ], + "childValidatorPlans": ["show_parameter_value_on_screen"] + } + ] + }, + { + ".desc:": "on shake gesture", + "name": "on_shake_gesture", + "threshold": 1, + "checks": [ + { + "validator": "blockFieldValueExists", + "fieldType": "NAME", + "fieldValue": "Gesture.Shake", + "blockType": "device_gesture_event" + } + ] + }, + { + ".desc": "declare a variable with name 'hand'", + "name": "variable_declared_called_hand", + "threshold": 1, + "checks": [ + { + "validator": "blockFieldValueExists", + "fieldType": "VAR", + "fieldValue": "hand", + "blockType": "variables_set" + } + ] + }, + { + ".desc": "read value of 'hand' variable", + "name": "hand_variable_accessed", + "threshold": 1, + "checks": [ + { + "validator": "blockFieldValueExists", + "fieldType": "VAR", + "fieldValue": "hand", + "blockType": "variables_get" + } + ] + }, + { + ".desc": "'hand' equal to number", + "name": "hand_equal_to_number", + "threshold": 1, + "checks": [ + { + "validator": "blockFieldValueExists", + "fieldType": "OP", + "fieldValue": "EQ", + "blockType": "logic_compare", + "childValidatorPlans": ["hand_variable_accessed"] + } + ] + }, + { + ".desc": "show icon when condition is true", + "name": "conditional_show_icon", + "threshold": 1, + "checks": [ + { + "validator": "blocksExist", + "blockCounts": [ + { + "blockId": "controls_if", + "count": 1 + } + ], + "childValidatorPlans": ["show_icon_on_screen"] + } + ] + }, + { + ".desc": "get sound level", + "name": "get_sound_level", + "threshold": 1, + "checks": [ + { + "validator": "blocksExist", + "blockCounts": [ + { + "blockId": "device_get_sound_level", + "count": 1 + } + ] + } + ] + }, + { + ".desc": "sound level greater than number", + "name": "soundlevel_greater_than_check", + "threshold": 1, + "checks": [ + { + "validator": "blockFieldValueExists", + "fieldType": "OP", + "fieldValue": "GT", + "blockType": "logic_compare", + "childValidatorPlans": ["get_sound_level", "math_num_exists"] + } + ] + }, + { + ".desc": "variable with name col is set to random number", + "name": "col_variable_set_random", + "threshold": 1, + "checks": [ + { + "validator": "blockFieldValueExists", + "fieldType": "VAR", + "fieldValue": "col", + "blockType": "variables_set", + "childValidatorPlans": ["device_random_used"] + } + ] + }, + { + ".desc": "variable with name row is set to random number", + "name": "row_variable_set_random", + "threshold": 1, + "checks": [ + { + "validator": "blockFieldValueExists", + "fieldType": "VAR", + "fieldValue": "row", + "blockType": "variables_set", + "childValidatorPlans": ["device_random_used"] + } + ] + }, + { + ".desc": "variable with name col is accessed", + "name": "col_variable_accessed", + "threshold": 1, + "checks": [ + { + "validator": "blockFieldValueExists", + "fieldType": "VAR", + "fieldValue": "col", + "blockType": "variables_get" + } + ] + }, + { + ".desc": "variable with name col is accessed", + "name": "row_variable_accessed", + "threshold": 1, + "checks": [ + { + "validator": "blockFieldValueExists", + "fieldType": "VAR", + "fieldValue": "row", + "blockType": "variables_get" + } + ] + }, + { + ".desc": "sound level check in if statement wtih two variables set blocks", + "name": "soundlevel_gt_condition", + "threshold": 1, + "checks": [ + { + "validator": "blocksExist", + "blockCounts": [ + { + "blockId": "controls_if", + "count": 1 + } + ], + "childValidatorPlans": [ + "soundlevel_greater_than_check", + "row_variable_set_random", + "col_variable_set_random" + ] + } + ] + }, + { + ".desc": "point block used for boolean check", + "name": "point_bool_check", + "threshold": 1, + "checks": [ + { + "validator": "blocksExist", + "blockCounts": [ + { + "blockId": "device_point", + "count": 1 + } + ], + "childValidatorPlans": ["row_variable_accessed", "col_variable_accessed"] + } + ] + }, + { + ".desc": "unplot block filled with row, col variables", + "name": "unplot_vars_used", + "threshold": 1, + "checks": [ + { + "validator": "blocksExist", + "blockCounts": [ + { + "blockId": "device_unplot", + "count": 1 + } + ], + "childValidatorPlans": ["col_variable_accessed", "row_variable_accessed"] + } + ] + }, + { + ".desc": "variable with name col is accessed", + "name": "col_add_num", + "threshold": 1, + "checks": [ + { + "validator": "blockFieldValueExists", + "fieldType": "OP", + "fieldValue": "ADD", + "blockType": "math_arithmetic", + "childValidatorPlans": ["col_variable_accessed", "math_num_exists"] + } + ] + }, + { + ".desc": "plot block filled with math add between row, num; and col variables", + "name": "plot_vars_used", + "threshold": 1, + "checks": [ + { + "validator": "blocksExist", + "blockCounts": [ + { + "blockId": "device_plot", + "count": 1 + } + ], + "childValidatorPlans": ["col_add_num", "row_variable_accessed"] + } + ] + }, + { + ".desc": "point block in if statement with unplot, plot in its body", + "name": "point_condition", + "threshold": 1, + "checks": [ + { + "validator": "blocksExist", + "blockCounts": [ + { + "blockId": "controls_if", + "count": 1 + } + ], + "childValidatorPlans": ["point_bool_check", "unplot_vars_used", "plot_vars_used"] + } + ] + }, + { + ".desc": "repeat loop nests two conditions in its body", + "name": "repeat_loop_nested_conditions", + "threshold": 1, + "checks": [ + { + "validator": "blocksExist", + "blockCounts": [ + { + "blockId": "controls_repeat_ext", + "count": 1 + } + ], + "childValidatorPlans": ["point_condition", "soundlevel_gt_condition"] + } + ] + }, + { + ".desc": "show icon on start", + "name": "show_icon_on_start", + "threshold": 1, + "checks": [ + { + "validator": "blocksExist", + "blockCounts": [ + { + "blockId": "pxt-on-start", + "count": 1 + } + ], + "childValidatorPlans": ["show_icon_on_screen"] + } + ] + }, + { + ".desc": "checks that the program aligns to the end tutorial program", + "name": "blow_away_completeness", + "threshold": 2, + "checks": [ + { + "validator": "blocksExist", + "blockCounts": [ + { + "blockId": "device_forever", + "count": 1 + } + ], + "childValidatorPlans": ["repeat_loop_nested_conditions"] + }, + { + "validator": "blocksExist", + "blockCounts": [ + { + "blockId": "pxt-on-start", + "count": 1 + } + ], + "childValidatorPlans": ["show_icon_on_screen"] + } + ] + } + ] +} diff --git a/docs/tours/editor-tour.md b/docs/tours/editor-tour.md new file mode 100644 index 00000000000..80e84cdf8cf --- /dev/null +++ b/docs/tours/editor-tour.md @@ -0,0 +1,69 @@ +# Editor Tour +* title: Editor tour +* description: This tour shows the user around the micro:bit editor, pointing out the toolbox, workspace, simulator, share button, and download button. + +## Welcome +* title: Welcome! +* description: New here? Take a tour of the editor! +* highlight: nothing +* location: center + +## Micro:bit Simulator +* title: Micro:bit Simulator +* description: See what your code looks like running on a micro:bit! +* highlight: simulator +* location: right + +## Toolbox +* title: Toolbox +* description: Drag out blocks of code from the Toolbox categories into the Workspace. +* highlight: toolbox +* location: right + +## Toolbox +* title: Toolbox +* description: Drag out snippets of code from the Toolbox categories into the Workspace. +* highlight: monaco toolbox +* location: right + +## Workspace +* title: Workspace +* description: Snap blocks of code together to build your program. +* highlight: workspace +* location: center + +## Workspace +* title: Workspace +* description: Write code to build your program. +* highlight: monaco workspace +* location: center + +## Share +* title: Share +* description: Create a link to your project to share with others. +* highlight: share +* location: below + +## Sign-In +* title: Sign-In +* description: Sign-in with your Microsoft, Google, or Clever account to save your projects to the cloud. +* highlight: sign in +* location: above + +## User Profile +* title: User Profile +* description: Come here to manage your user profile. +* highlight: avatar +* location: above + +## Download +* title: Download +* description: Download your program onto the micro:bit. +* highlight: download +* location: above + +## Congrats +* title: Congratulations! +* description: You've completed the editor tour! 🤩🏆🤩 Happy coding! +* highlight: everything +* location: center \ No newline at end of file diff --git a/docs/translate.md b/docs/translate.md index 9cd7e4964c5..3928fd6e78d 100644 --- a/docs/translate.md +++ b/docs/translate.md @@ -4,53 +4,82 @@ ### ~ hint -Looking to help translate the site for **[microbit.org](http://microbit.org)**? Try http://translate.microbit.org/ to help the Microbit Foundation! +#### Help translate + +Looking to help translate the site for **[microbit.org](http://microbit.org)**? Try http://translate.microbit.org/ to help the Micro:bit Foundation! ### ~ ## #target-files -The following lists provide a guide to which translation files and folders relate to the **MakeCode for @boardname@** editor. The links here are to the [English](https://crowdin.com/project/kindscript/en#) source files just to show you the location of the files in the folder structure. Of course, you will translate in your selected language instead. +When you select your language from the [MakeCode](https://crowdin.com/project/makecode) project homepage, you'll find all of the localization files for MakeCode shown in a folder tree. The strings to translate for the @boardname@ are found in the files under the **microbit** folder for the current language. -### Editor +![microbit strings files](/static/mb/translate/crowdin-folder.png) -Files related to the editor: -* [strings.json](https://crowdin.com/translate/kindscript/32/en-en) - Strings common and shared by all MakeCode editors +Localization files are present in two different forms, JSON and markdown. The JSON files (those you see with the **.json** ending in their names) contain localizable strings related to both the editor UI and the text shown on the programming code blocks. All of the markdown files (those with **.md** at the end of their names) are documents for reference, projects, tutorials, help information, etc. -![strings.json file in Crowdin UI](/static/mb/translate/stringsfile.png) +The files listed in the following sections provide a guide to how each of the translation files and folders relate to the **MakeCode for @boardname@** editor. + +### Editor -* [target-strings.json](https://crowdin.com/translate/kindscript/1922/en-en) - Strings custom to the @boardname@ editor interface +There are a few files that are specific to the MakeCode editor itself. These contain strings for the editor UI and the simulator. They are essential to translate and should be prioritized before the other files. -![target-strings.json file in Crowdin UI](/static/mb/translate/targetstringsfile.png) +| File | Description | +| - | - | +| strings.json | Common strings that are shared by all MakeCode editors. **Note**: This file is located at the MakeCode project's root folder rather than under **microbit** | +| target-strings.json | Strings custom to the @boardname@ editor interface | +| sim-strings.json | Strings for the @boardname@ simulator | +
-This is an example of the editor with it's interface elements localized: +This is an example of the editor with its interface elements localized: ![Translated editor elements](/static/mb/translate/target-strings.jpg) ### Blocks -* [core-jsdoc-strings.json](https://crowdin.com/translate/kindscript/66/en-en) - Description text for code elements of the [basic](/reference/basic) and core [blocks](/blocks) -* [core-strings.json](https://crowdin.com/translate/kindscript/65/en-en) - Display text for the [basic](/reference/basic) and core [blocks](/reference/blocks) -* [radio-jsdoc-strings.json](https://crowdin.com/translate/kindscript/64/en-en) - Description text for code elements of the [radio](/reference/radio) blocks -* [radio-strings.json](https://crowdin.com/translate/kindscript/63/en-en) - Display text for the [radio](/reference/radio) blocks -* [devices-jsdoc-strings.json](https://crowdin.com/translate/kindscript/62/en-en) - Description text for code elements of the [devices](/reference/devices) blocks -* [devices-strings.json](https://crowdin.com/translate/kindscript/61/en-en) - Display text for the [devices](/reference/devices) blocks -* [radio-broadcast-jsdoc-strings.json](https://crowdin.com/translate/kindscript/5032/en-en) - Description text for code elements of the radio broadcast blocks -* [radio-broadcast-strings.json](https://crowdin.com/translate/kindscript/5030/en-en) - Display text for the radio broadcast blocks -* [servo-jsdoc-strings.json](https://crowdin.com/translate/kindscript/5036/en-en) - Description text for code elements of the [servo](/reference/servos) blocks -* [servo-strings.json](https://crowdin.com/translate/kindscript/5034/en-ens) - Display text for the [servo](/reference/servos) blocks -* [bluetooth-jsdoc-strings.json](https://crowdin.com/translate/kindscript/60/en-en) - Description text for code elements of the [bluetooth](/reference/bluetooth) blocks -* [bluetooth-strings.json](https://crowdin.com/translate/kindscript/59/en-en) - Display text for the [bluetooth](/reference/bluetooth) blocks - -![screenshot of library file in Crowdin UI](/static/mb/translate/libsfiles.png) - -Here are some translated blocks: +The strings for the programming code blocks all have names in the form of '_name_-strings.json' and '_name_-jsdoc-strings.json'. The _name_ part of the filename often refers to which set of blocks or the extension that the blocks come from. + +| File | Description | +| - | - | +| core-jsdoc-strings.json | Description text for code elements of the [basic](/reference/basic) and core [blocks](/blocks). **Note**: this file contains strings for the fundamental set of coding blocks and should be prioritized over the other strings files for blocks | +| core-strings.json | Display text for the [basic](/reference/basic) and core [blocks](/reference/blocks). **Note**: this file contains strings for the fundamental set of coding blocks and should be prioritized over the other strings files for blocks | +| radio-jsdoc-strings.json | Description text for code elements of the [radio](/reference/radio) blocks | +| radio-strings.json | Display text for the [radio](/reference/radio) blocks | +| radio-broadcast-jsdoc-strings.json | Description text for code elements of the radio broadcast blocks | +| radio-broadcast-strings.json | Display text for the radio broadcast blocks | +| servo-jsdoc-strings.json | Description text for code elements of the [servo](/reference/servos) blocks | +| servo-strings.json | Display text for the [servo](/reference/servos) blocks | +| bluetooth-jsdoc-strings.json | Description text for code elements of the [bluetooth](/reference/bluetooth) blocks | +| bluetooth-strings.json | Display text for the [bluetooth](/reference/bluetooth) blocks | +| devices-jsdoc-strings.json | Description text for code elements of the _connected devices_ blocks | +| devices-strings.json | Display text for the _connected devices_ blocks | +| flashlog-jsdoc-strings.json | Description text for code elements of the _flashlog_ blocks | +| flashlog-strings.json | Display text for the _flashlog_ blocks | +| datalogger-jsdoc-strings.json | Description text for code elements of the [datalogger](/reference/datalogger) blocks | +| datalogger-strings.json | Display text for the [datalogger](/reference/datalogger) blocks | +| jacdac-jsdoc-strings.json | Description text for code elements of the _jacdac_ blocks | +| jacdac-strings.json | Display text for the _jacdac_ blocks | +| color-jsdoc-strings.json | Description text for code elements of the _color_ blocks | +| color-strings.json | Display text for the _color_ blocks | +| microphone-jsdoc-strings.json | Description text for code elements of the _microphone_ blocks | +| microphone-strings.json | Display text for the _microphone_ blocks | +| settings-jsdoc-strings.json | Description text for code elements of the _settings_ blocks | +| settings-strings.json | Display text for the _settings_ blocks | +
+ +Here are some examples of translated blocks: ![Translated block text](/static/mb/translate/block-text.jpg) ### Document pages -* [docs](https://crowdin.com/translate/kindscript/en#/microbit/docs) - Documentation pages for projects, courses, lessons, and code block reference +Document pages contain the text for any markdown page available on the MakeCode editor site. These include code block reference, projects, tutorials, how-to information, etc. + +| File | Description | +| - | - | +| docs | Documentation pages for projects, courses, lessons, and code block reference | +| libs | Documentation pages for code block reference and other information related to built-in extensions like _servo_ and _datalogger_ | +
Here's an example of a translated document page for a course lesson: diff --git a/docs/tutorials-v2.md b/docs/tutorials-v2.md new file mode 100644 index 00000000000..7f55356b8ec --- /dev/null +++ b/docs/tutorials-v2.md @@ -0,0 +1,45 @@ +# Projects + +Here are some cool tutorials to get you started with your new @boardname@ (V2)! + +## Basic + +```codecard +[{ + "name": "Pet Hamster", + "url":"/projects/v2-pet-hamster", + "description": "Interact with your very own micro:bit hamster named Cyrus.", + "imageUrl": "/static/mb/projects/pet-hamster.png", + "cardType": "tutorial" +}, { + "name": "Countdown", + "url":"/projects/v2-countdown", + "description": "Create a musical countdown sequence.", + "imageUrl": "/static/mb/projects/countdown.png", + "cardType": "tutorial" +}, { + "name": "Morse Chat", + "url":"/projects/v2-morse-chat", + "description": "Learn how to send morse code messages to a pig named Sky.", + "imageUrl": "/static/mb/projects/morse-chat.png", + "cardType": "tutorial" +}, { + "name": "Clap Lights", + "url":"/projects/v2-clap-lights", + "description": "Turn your micro:bit's lights on or off when you clap.", + "imageUrl": "/static/mb/projects/clap-lights.png", + "cardType": "tutorial" +}, { + "name": "Blow Away", + "url":"/projects/v2-blow-away", + "description": "Use the sound of your breath to blow a ghost named Haven away.", + "imageUrl": "/static/mb/projects/blow-away.png", + "cardType": "tutorial" +}, { + "name": "Cat Napping", + "url":"/projects/v2-cat-napping", + "description": "Use data logging to help Lychee find sun spots.", + "imageUrl": "/static/mb/projects/cat-napping/1_lychee.png", + "cardType": "tutorial" +}] +``` \ No newline at end of file diff --git a/docs/tutorials.md b/docs/tutorials.md index 00e593f8e4e..167f5a3a766 100644 --- a/docs/tutorials.md +++ b/docs/tutorials.md @@ -14,7 +14,7 @@ Here are some cool tutorials to get you started with your @boardname@! "cardType": "tutorial", "label": "New? Start Here!", "labelClass": "purple ribbon large", - "youTubeId": "NvEOKZ8wh9s", + "youTubeId": "hiERNxxfxJQ", "otherActions": [{ "url": "/projects/spy/flashing-heart", "editor": "py", @@ -30,7 +30,7 @@ Here are some cool tutorials to get you started with your @boardname@! "imageUrl": "/static/mb/projects/name-tag.png", "url": "/projects/name-tag", "cardType": "tutorial", - "youTubeId": "xpRI5jjQ31E", + "youTubeId": "tOgVbOG5QAo", "otherActions": [{ "url": "/projects/spy/name-tag", "editor": "py", @@ -47,9 +47,9 @@ Here are some cool tutorials to get you started with your @boardname@! "imageUrl": "/static/mb/projects/a2-buttons.png", "largeImageUrl": "/static/mb/projects/smiley-buttons/sim.gif", "cardType": "tutorial", - "youTubeId": "BgDxz3M7JIM", + "youTubeId": "kZOTlXGIzPI", "otherActions": [{ - "url": "/projects/spy/smiley-buttons", + "url": "/projects/python/smiley-buttons", "editor": "py", "cardType": "tutorial" }, { @@ -63,7 +63,7 @@ Here are some cool tutorials to get you started with your @boardname@! "description": "Shake the dice and see what number comes up!", "imageUrl": "/static/mb/projects/dice.png", "cardType": "tutorial", - "youTubeId": "OmrmjtOm_sQ", + "youTubeId": "8lrlMwWDPo8", "otherActions": [{ "url": "/projects/spy/dice", "editor": "py", @@ -79,7 +79,7 @@ Here are some cool tutorials to get you started with your @boardname@! "description": "Is the micro:bit is feeling the love, see how much!", "imageUrl":"/static/mb/projects/a3-pins.png", "cardType": "tutorial", - "youTubeId": "1IYsy0_9n8g", + "youTubeId": "sEIRwv2Aa2Q", "otherActions": [{ "url": "/projects/spy/love-meter", "editor": "py", @@ -95,7 +95,7 @@ Here are some cool tutorials to get you started with your @boardname@! "description": "Build your own social network made of micro:bits.", "imageUrl": "/static/mb/projects/a9-radio.png", "cardType": "tutorial", - "youTubeId": "5XqsGROG2fI", + "youTubeId": "egTeIghYXak", "otherActions": [{ "url": "/projects/spy/micro-chat", "editor": "py", diff --git a/docs/types/playable.md b/docs/types/playable.md new file mode 100644 index 00000000000..4e2808dab48 --- /dev/null +++ b/docs/types/playable.md @@ -0,0 +1,59 @@ +# playable + +The **playable** data object provides a common format to play tones, melodies, and songs. Each of these music sources are created in different ways but are transformed into playable objects so that a single playback method is used to [play](/refernece/music/play) them. + +## Music sources for playable objects + +The blocks used to create playable music sources are the following: + +### Tone + +A tone is a musical note, or a sound frequency, and a duration. The duration is often set as the length of a `beat`. + +```block +music.tonePlayable(262, music.beat(BeatFraction.Whole)) +``` + +### Melody + +Melodies are a series of notes and a tempo to play them at. + +```block +music.stringPlayable("D F E A E A C B ", 120) +``` + +### Sound Expression + +A sound expression is set of parameters that describe a **[sound](/types/sound)** that will last for some amount of time. These parameters specify a base waveform, frequency range, sound volume, and effects. + +```block +music.play(music.createSoundExpression(WaveShape.Sine, 5000, 0, 255, 0, 500, SoundExpressionEffect.None, InterpolationCurve.Linear), music.PlaybackMode.UntilDone) +``` + +## Play the music + +In your programs, you can simply use the ``||music:play||`` blocks for each playable object. Like this one for tone: + +```block +music.play(music.tonePlayable(262, music.beat(BeatFraction.Whole)), music.PlaybackMode.UntilDone) +``` + +## Example + +Put 2 different playable music sources in an array. Play one after the other. + +```blocks +let playables = [ +music.tonePlayable(262, music.beat(BeatFraction.Whole)), +music.stringPlayable("D F E A E A C B ", 120) +] +for (let someMusic of playables) { + music.play(someMusic, music.PlaybackMode.UntilDone) + basic.pause(500) +} +``` + +## See also + +[play](/reference/music/play), [tone playable](/reference/music/tone-playable) +[string playable](/reference/music/string-playable), [create sound expression](/reference/music/create-sound-expression) \ No newline at end of file diff --git a/docs/types/sound.md b/docs/types/sound.md new file mode 100644 index 00000000000..fbc999a68c6 --- /dev/null +++ b/docs/types/sound.md @@ -0,0 +1,136 @@ +# Sound + +A **Sound** is a data object that is created from a sound expression. A sound expression is group of parameters that define a sound, such as wave shape, sound volume, frequency, and duration. + +A sound is generated from an expression based on a fundamental wave shape, or waveform. To make a sound wave, the sound data must change from a high peak to a low trough over a period of time and repeat. The peaks and troughs are the positive amplitudes and negative amplitudes of the wave across the zero line. The volume controls the amplitude of the wave. + +When the sound is played on a speaker or headphones, the vibrations create the pressures our ears detect as sound. + +### ~ hint + +#### Sounds and Sound Expressions + +In code, a **Sound** type is a complex data object that includes data for all the elements that represent a sound. This includes information about the frequencies and volumes at various points in time for the duration of the sound. A **SoundExpression** is another data type that helps create a **Sound**. It has the elements of how to make the sound. Many of them you specify when you edit a sound. + +Code for creating and playing a sound from a sound expression could look like this: + +```typescript-ignore +let mySound = music.createSoundExpression(WaveShape.Sine, 2000, 0, 1023, 0, 500, SoundExpressionEffect.None, InterpolationCurve.Linear) +music.play(mySound, music.PlaybackMode.UntilDone) +``` + +### ~ + +## Sound Editing + +When you click on the waveform in the ``||music:play sound||`` block, the sound editor will display. The sound editor defines the sound expression parameter with choices for the **waveform**, sound **duration** time, **frequency** range, **volume** range, **effect**, and **interpolation**. + +![Sound Editor](/static/types/sound/sound-editor.png) + +Both the frequency and volume can start and end with different values across the duration of the sound. + +## Wave shape + +The wave shape is chosen to create a natural sound or a synthetic sound. Some wave shapes can also serve to generate signals when played to a pin instead of a speaker or headphones. + +### Sine wave + +The waveform that matches natural sound is the sine wave. This is the wave type in music and voice. + +![Sine wave](/static/types/sound/sine-wave.png) + +### Sawtooth wave + +A sawtooth wave has a vertical rising edge and a linear falling edge. It's shape looks like the teeth on a saw. + +![Sawtooth wave](/static/types/sound/sawtooth-wave.png) + +### Triangle wave + +The triangle wave is has symmetrical a rising and a falling edge. It makes the shape of triangles in the waveform. + +![Triangle wave](/static/types/sound/triangle-wave.png) + +### Square wave + +A square wave has both verical rising and falling edges with a flat section on the top and bottom. The flat sections match the volume set for the sound. Square waves are sometimes used to represent digital data and will make an "electronic" sound. + +![Square wave](/static/types/sound/square-wave.png) + +### Noise wave + +The noise wave is created using random frequencies and volume. Setting the frequency parameters for the sound expression creates a "tuning" range for the noise sound effect. + +![Noise wave](/static/types/sound/noise-wave.png) + +## Duration + +The sound has a length of time that it plays for. This is set as a number of milliseconds (**ms**). + +## Volume + +The volume controls the loudness (amplitude) of the sound. The sound can start with one volume setting and end with another. It can begin loud and end quiet, or the other way around. The volume control has start and end points that can be adjusted higher and lower. Grab them and move them up or down. + +### High to low + +![Volume from high to low](/static/types/sound/volume-hilo.png) + +### Low to High + +![Volume from low to high](/static/types/sound/volume-lohi.png) + +### Constant volume + +![Constant volume](/static/types/sound/volume-constant.png) + +## Frequency + +Frequency is how fast a wave repeats itself from the zero line to its peak down to its trough and back to the zero line. If it does this 1000 times in one second then the frequency has 1000 cycles per second and is measured in units of Hertz (1000 Hz). The frequency of the sound at any point in time is its current _pitch_. Musical notes and parts of speech are different frequencies that last for short periods of time in a sound. + +A sound expression has both a starting frequency and an ending frequency. The frequency can start low and end high, start high and end low, or remain the same for the duration of the sound. + +### High to low + +![Frequency from high to low](/static/types/sound/freq-hilo.png) + +### Low to High + +![Frequency from low to high](/static/types/sound/freq-lohi.png) + +### Effect + +Effects add small changes to the waveform but can make a big change in how it sounds to a listener. There are a few effects available to apply to a sound. + +* **Tremolo**: add slight changes in volume of the sound expression. + +>![Tremolo effect setting](/static/types/sound/effect-tremolo.png) + +* **Vibrato**: add slight changes in frequency to the sound expression. + +>![Vibrato effect setting](/static/types/sound/effect-vibrato.png) + +* **Warble**: similar to Vibrato but with faster variations in the frequency changes. + +>![Warble effect setting](/static/types/sound/effect-warble.png) + +### Interpolation + +Interpolation is how the sound expression will make the changes in frequency or volume of the sound. These changes can occur at a constant rate along duration of the sound or more suddenly at the beginning. + +* **Linear**: The change in frequency is constant for the duration of the sound. + +>![Frequency from low to high](/static/types/sound/interp-linear.png) + +* **Curve**: The change in frequency is faster at the beginning of the sound and slows toward the end. + +>![Frequency from low to high](/static/types/sound/interp-curve.png) + +* **Logarithmic**: The change in frequency is rapid during the very first part of the sound. + + +>![Frequency from low to high](/static/types/sound/interp-log.png) + +## See also + +[play](/reference/music/play), +[create sound expression](/reference/music/create-sound-expression) \ No newline at end of file diff --git a/docs/types/string.md b/docs/types/string.md index 11ae1da6f92..96787118b8a 100644 --- a/docs/types/string.md +++ b/docs/types/string.md @@ -1,9 +1,27 @@ # @extends -## #intro +## #create -## ~ hint +In the ``||variables:Variables||`` category of the **Toolbox** you can create new variable: -For the @boardname@, ASCII character codes 32 to 126 are supported; letters, digits, punctuation marks, and a few symbols. All other character codes appear as a ? on the [LED screen](/device/screen). +![Create a new string variable](/static/blocks/variables/string.gif) -## ~ \ No newline at end of file +Here's how to create a string variable using the Toolbox: + +1. Click ``||variables:Variables||`` in the Toolbox. +2. Click on **Make a Variable...**. +3. Choose a name for your variable, type it in, and click **Ok**. +4. Drag the new ``||variables:set||`` block into your code. +5. Click on the ``||text:Text||`` drawer in the Toolbox and find the ``||text:" "||`` block. +6. Drag the ``||text:" "||`` block into the value slot in of your variable ``||variables:set||`` block. + +## Characters you use in strings #custom + +### ~ hint + +#### Character sets + +The available characters to use for a language is called the _character set_. Each character in the set has a number code to match it with. +To display characters on the [LED screen](/device/screen), the @boardname@, uses the "ASCII" character codes of `32` to `126`; letters, digits, punctuation marks, and a few symbols. All other character codes appear as a `?` on the LED screen. + +### ~ \ No newline at end of file diff --git a/docs/windows-app.md b/docs/windows-app.md new file mode 100644 index 00000000000..154c33ae609 --- /dev/null +++ b/docs/windows-app.md @@ -0,0 +1,11 @@ +# Windows App Deprecation + +The original MakeCode for micro:bit Windows app has been deprecated. For continued support, please use our new app: https://apps.microsoft.com/store/detail/microsoft-makecode-for-microbit/9NMQDQ2XZKWK + +## Moving Projects + +If you want to keep the projects you saved with the old app and use them again in the new app, you will need to transfer them. + +1. Share the project from the app using the **Share** option. +2. Copy the shared project URL that the app gives you. +3. On the new MakeCode app homepage, click on **Import** to open the shared project with the URL you just copied. diff --git a/editor/dialogs.tsx b/editor/dialogs.tsx index 7d3536ee689..14bddb18634 100644 --- a/editor/dialogs.tsx +++ b/editor/dialogs.tsx @@ -1,113 +1,5 @@ import * as React from "react"; -export function renderUsbPairDialog(firmwareUrl?: string, failedOnce?: boolean): JSX.Element { - const boardName = pxt.appTarget.appTheme.boardName || "???"; - const helpUrl = pxt.appTarget.appTheme.usbDocs; - firmwareUrl = failedOnce && `${helpUrl}/webusb/troubleshoot`; // todo mo - - const instructions =
-
-
-
-
-
-
- {lf("Comic -
-
-
- 1 - {lf("Connect the {0} to your computer with a USB cable", boardName)} -
- {lf("Use the microUSB port on the top of the {0}", boardName)} -
-
-
-
-
-
-
- {lf("Comic -
-
-
- 2 - {lf("Pair your {0}", boardName)} -
- {lf("Click 'Pair device' below and select BBC micro:bit CMSIS-DAP or DAPLink CMSIS-DAP from the list")} -
-
-
-
-
-
-
-
; - - if (!firmwareUrl) return instructions; - - return
-
-
{lf("Update Firmware")}
- {lf("You must have version 0249 or above of the firmware")} -
- {lf("Comic -
- {lf("Check Firmware")} -
-
- {instructions} -
-
; -} - -export function renderBrowserDownloadInstructions(): JSX.Element { - const boardName = pxt.appTarget.appTheme.boardName || lf("device"); - const boardDriveName = pxt.appTarget.appTheme.driveDisplayName || pxt.appTarget.compile.driveName || "???"; - return
-
-
-
-
-
-
-
-
- {lf("Comic -
-
-
- 1 - {lf("Connect the {0} to your computer with a USB cable", boardName)} -
- {lf("Use the microUSB port on the top of the {0}", boardName)} -
-
-
-
-
-
-
- {lf("Comic -
-
-
- 2 - {lf("Move the .hex file to the {0}", boardName)} -
- {lf("Locate the downloaded .hex file and drag it to the {0} drive", boardDriveName)} -
-
-
-
-
-
-
-
-
-
; -} - export function cantImportAsync(project: pxt.editor.IProjectView) { // this feature is support in v0 only return project.showModalDialogAsync({ @@ -121,3 +13,50 @@ export function cantImportAsync(project: pxt.editor.IProjectView) { ] }).then(() => project.openHome()) } + + +export async function showProgramTooLargeErrorAsync(variants: string[], confirmAsync: (opts: any) => Promise, saveOnly?: boolean) { + if (variants.length !== 2) { + if (variants[0] !== "mbcodal") return undefined; + await confirmAsync({ + header: lf("Oops, there was a problem downloading your code"), + body: lf("Great coding skills! Unfortunately, your program is too large to fit on a micro:bit V2đŸ˜ĸ. You can go back and try to make your program smaller, or continue to use the simulator to run your code."), + bigHelpButton: true, + hideAgree: true, + disagreeLbl: lf("Go Back"), + disagreeClass: "positive", + }); + return undefined + } + + if (pxt.packetio.isConnected() && pxt.packetio.deviceVariant() === "mbcodal" && !saveOnly) { + // connected micro:bit V2 will be flashed; don't give warning dialog + return { + recompile: true, + useVariants: ["mbcodal"] + } + } + + const choice = await confirmAsync({ + header: lf("Oops, there was a problem downloading your code"), + body: lf("Great coding skills! Unfortunately, your program is too large to fit on a micro:bit V1đŸ˜ĸ. You can go back and try to make your program smaller, or you can download your program onto a micro:bit V2."), + bigHelpButton: true, + agreeLbl: lf("Go Back"), + agreeClass: "cancel", + agreeIcon: "cancel", + disagreeLbl: lf("Download for V2 only"), + disagreeClass: "positive", + disagreeIcon: "checkmark" + }); + + if (!choice) { + return { + recompile: true, + useVariants: ["mbcodal"] + } + } + return { + recompile: false, + useVariants: [] + } +} diff --git a/editor/extension.tsx b/editor/extension.tsx index d6c0e20bdfb..91179f494f0 100644 --- a/editor/extension.tsx +++ b/editor/extension.tsx @@ -1,8 +1,7 @@ /// -/// /// /// -/// +/// /// import * as dialogs from "./dialogs"; import * as flash from "./flash"; @@ -24,21 +23,7 @@ pxt.editor.initExtensionsAsync = function (opts: pxt.editor.ExtensionOptions): P }; const res: pxt.editor.ExtensionResult = { - hexFileImporters: [{ - id: "blockly", - canImport: data => data.meta.cloudId == "microbit.co.uk" && data.meta.editor == "blockly", - importAsync: (project, data) => { - pxt.tickEvent('import.legacyblocks.redirect'); - return dialogs.cantImportAsync(project); - } - }, { - id: "td", - canImport: data => data.meta.cloudId == "microbit.co.uk" && data.meta.editor == "touchdevelop", - importAsync: (project, data) => { - pxt.tickEvent('import.legacytd.redirect'); - return dialogs.cantImportAsync(project); - } - }] + hexFileImporters: [] }; pxt.usb.setFilters([{ @@ -55,7 +40,6 @@ pxt.editor.initExtensionsAsync = function (opts: pxt.editor.ExtensionOptions): P res.mkPacketIOWrapper = flash.mkDAPLinkPacketIOWrapper; res.blocklyPatch = patch.patchBlocks; - res.renderBrowserDownloadInstructions = dialogs.renderBrowserDownloadInstructions; - res.renderUsbPairDialog = dialogs.renderUsbPairDialog; + res.showProgramTooLargeErrorAsync = dialogs.showProgramTooLargeErrorAsync; return Promise.resolve(res); } diff --git a/editor/flash.ts b/editor/flash.ts index 88643237961..75a8c7e69d3 100644 --- a/editor/flash.ts +++ b/editor/flash.ts @@ -6,6 +6,8 @@ const dataAddr = 0x20002000; const stackAddr = 0x20001000; const FULL_FLASH_TIMEOUT = 100000; // 100s const PARTIAL_FLASH_TIMEOUT = 60000; // 60s +const CONNECTION_CHECK_TIMEOUT = 2000; // 2s +const RETRY_DAP_CMD_TIMEOUT = 50; // .05s const flashPageBIN = new Uint32Array([ 0xbe00be00, // bkpt - LR is set to this @@ -38,6 +40,8 @@ function log(msg: string) { pxt.debug(`dap ${ts}: ${msg}`) } const logV = /webusbdbg=1/.test(window.location.href) ? log : (msg: string) => { } +const setBaudRateOnConnection = !/webusbbaud=0/.test(window.location.href) +const resetOnConnection = !/webusbreset=0/.test(window.location.href) function murmur3_core(data: Uint8Array) { let h0 = 0x2F9BE6CC; @@ -59,30 +63,43 @@ function murmur3_core(data: Uint8Array) { return [h0, h1] } +function bufferConcat(a: Uint8Array, b: Uint8Array) { + const r = new Uint8Array(a.length + b.length) + r.set(a, 0) + r.set(b, a.length) + return r +} + class DAPWrapper implements pxt.packetio.PacketIOWrapper { + private initialized = false familyID: number; private dap: DapJS.DAP; private cortexM: DapJS.CortexM - private cmsisdap: any; - private flashing = false; private flashAborted = false; - private readSerialId = 0; + private connectionId = 0; private pbuf = new pxt.U.PromiseBuffer(); private pageSize = 1024; private numPages = 256; - private usesCODAL = false; + + private usesCODAL: boolean = undefined; + // we don't know yet if jacdac was compiled in the hex + private jacdacInHex: boolean = undefined private forceFullFlash = /webusbfullflash=1/.test(window.location.href); - private useJACDAC = true; onSerial = (buf: Uint8Array, isStderr: boolean) => { }; onCustomEvent = (type: string, payload: Uint8Array) => { }; constructor(public readonly io: pxt.packetio.PacketIO) { this.familyID = 0x0D28; // this is the microbit vendor id, not quite UF2 family id - this.io.onDeviceConnectionChanged = (connect) => { + this.io.onDeviceConnectionChanged = async (connect) => { log(`device connection changed`); - this.disconnectAsync() - .then(() => connect && this.reconnectAsync()); + await this.disconnectAsync() + // we don't know what's being connected + this.usesCODAL = undefined + this.jacdacInHex = undefined + + if (!connect) return; + await this.reconnectAsync() } this.io.onData = buf => { @@ -93,45 +110,123 @@ class DAPWrapper implements pxt.packetio.PacketIOWrapper { this.allocDAP(); } - icon = "usb"; - - private startReadSerial() { - const rid = this.readSerialId; - log(`start read serial ${rid}`) - const readSerial = async () => { - try { - while (true) { - if (rid != this.readSerialId) break + icon = "xicon microbit"; - const r = await this.dapCmdNums(0x83) - if (rid != this.readSerialId) break + private pendingSerial: Uint8Array + private lastPendingSerial: number - const len = r[1] - const hasData = len > 0 - if (hasData && this.onSerial) - this.onSerial(r.slice(2, len + 2), false) + private processSerialLine(line: Uint8Array) { + if (this.onSerial) { + try { + // catch encoding bugs + this.onSerial(line, false) + } + catch (err) { + log(`serial decoding error: ${err.message}`); + pxt.tickEvent("hid.flash.serial.decode.error"); + console.error({ err, line }) + } + } + } - await this.jacdacProcess(hasData) + private async readSerial(): Promise { + let buf = await this.dapCmdNums(0x83) + const len = buf[1] + // concat received data with previous data + if (len) { + buf = buf.slice(2, 2 + len) + if (this.pendingSerial) buf = bufferConcat(this.pendingSerial, buf) + let ptr = 0 + let beg = 0 + while (ptr < buf.length) { + if (buf[ptr] == 10 || buf[ptr] == 13) { + ptr++; + // eat \r\n + while (ptr < buf.length && (buf[ptr] == 10 || buf[ptr] == 13)) + ptr++; + const line = buf.slice(beg, ptr) + if (line.length) + this.processSerialLine(line); + beg = ptr } + else + ptr++ + } + buf = buf.slice(beg) + this.pendingSerial = buf.length ? buf : null + if (this.pendingSerial) { + this.lastPendingSerial = Date.now() + //logV(`pending serial ${this.pendingSerial.length}`) + } + } else if (this.pendingSerial) { + const d = Date.now() - this.lastPendingSerial + if (d > 500) { + this.processSerialLine(this.pendingSerial) + this.pendingSerial = null + this.lastPendingSerial = undefined + } + } + return len + } - log(`stopped serial reader ${rid}`) + private startReadSerial(connectionId: number) { + const startTime = Date.now(); + log(`start read serial ${connectionId}`) + const readSerialLoop = async () => { + try { + let numSer = 0 + let numEv = 0 + while (connectionId === this.connectionId) { + numSer = await this.readSerial() + // we need to read jacdac in a tight loop + // so we don't miss any event + if (this.xchgAddr) + numEv = await this.jacdacProcess() + else + numEv = 0 + + // no data on either side, wait as little as possible + // the browser will eventually throttle this call + // https://developer.mozilla.org/en-US/docs/Web/API/setTimeout#reasons_for_delays_longer_than_specified + if (!numSer && !numEv) + await pxt.U.delay(0) + } + log(`stopped serial reader ${connectionId}`) } catch (err) { - log(`read error: ${err.message}`); - if (rid != this.readSerialId) { - log(`stopped serial reader ${rid}`) + log(`serial error ${connectionId}: ${err.message}`); + console.error(err) + if (connectionId != this.connectionId) { + log(`stopped serial reader ${connectionId}`) } else { - this.disconnectAsync(); // force disconnect + pxt.tickEvent("hid.flash.serial.error"); + const timeRunning = Date.now() - startTime + await this.disconnectAsync(); // force disconnect + // if we've been running for a while, try reconnecting + if (timeRunning > 1000) { + log(`auto-reconnect`) + try { + await this.reconnectAsync(); + } catch (e) { + if (e.type === "devicenotfound") + return + throw e + } + } } } + finally { + this.pendingSerial = undefined + this.lastPendingSerial = undefined + } } - readSerial(); + readSerialLoop(); } - private stopSerialAsync() { - log(`cancelling serial reader ${this.readSerialId}`) - this.readSerialId++; - return Promise.delay(200); + private stopReadersAsync() { + log(`cancelling connection ${this.connectionId}`) + this.connectionId++; + return pxt.Util.delay(200); } private allocDAP() { @@ -143,58 +238,139 @@ class DAPWrapper implements pxt.packetio.PacketIOWrapper { read: () => this.recvPacketAsync(), //sendMany: sendMany }); - this.cmsisdap = (this.dap as any).dap; this.cortexM = new DapJS.CortexM(this.dap); } get binName() { - return (this.usesCODAL ? "mbcodal-" : "mbdal-") + pxtc.BINARY_HEX; + return `${this.devVariant}-${pxtc.BINARY_HEX}`; + } + + get devVariant() { + if (this.usesCODAL === undefined) + console.warn('try to access codal information before it is computed') + return this.usesCODAL ? "mbcodal" : "mbdal"; + } + + unsupportedParts() { + if (this.usesCODAL === undefined) + console.warn('try to access codal information before it is computed') + if (!this.usesCODAL) { + return ["logotouch", "builtinspeaker", "microphone", "flashlog", "v2"] + } + return []; + } + + isConnected(): boolean { + return this.io.isConnected() && this.initialized + } + + isConnecting(): boolean { + return this.io.isConnecting() || (this.io.isConnected() && !this.initialized) + } + + private async getBaudRate() { + const readSerialSettings = new Uint8Array([0x81]) // get serial settings + const serialSettings = await this.dapCmd(readSerialSettings) + const baud = (serialSettings[4] << 24) + (serialSettings[3] << 16) + (serialSettings[2] << 8) + serialSettings[1] + return baud + } + + private async setBaudRate() { + const currentBaudRate = await this.getBaudRate() + if (currentBaudRate === 115200) { + log(`baud rate already set to 115200`) + return + } + log(`set baud rate to 115200`) + const baud = new Uint8Array(5) + baud[0] = 0x82 // set baud + pxt.HF2.write32(baud, 1, 115200) + await this.dapCmd(baud) + // setting the baud rate on serial may reset NRF (depending on daplink version), so delay after + await pxt.Util.delay(200); + } + + private async readPageSize() { + const res = await this.readWords(0x10000010, 2); + this.pageSize = res[0] + this.numPages = res[1] + log(`page size ${this.pageSize}, num pages ${this.numPages}`); } async reconnectAsync(): Promise { log(`reconnect`) + this.initialized = false this.flashAborted = false; + this.io.onConnectionChanged() function stringResponse(buf: Uint8Array) { return pxt.U.uint8ArrayToString(buf.slice(2, 2 + buf[1])) } - await this.stopSerialAsync() + await this.stopReadersAsync() + const connectionId = this.connectionId this.allocDAP(); // clean dap apis await this.io.reconnectAsync() - // before calling into dapjs, we use our dapCmdNums() a few times, which which will make sure the responses - // to commends from previous sessions (if any) are flushed - const info = await this.dapCmdNums(0x00, 0x04) // info - log(`daplink version: ${stringResponse(info)}`) + await this.clearCommandsAsync() + + // halt before reading from dap + // to avoid interference from data logger + await this.cortexM.halt() + + const info = await this.getDaplinkVersionAsync(); // info + const daplinkVersion = stringResponse(info); + log(`daplink version: ${daplinkVersion}`); const r = await this.dapCmdNums(0x80) this.usesCODAL = r[2] == 57 && r[3] == 57 && r[5] >= 51; - if (!this.usesCODAL) - this.useJACDAC = false; - log(`bin name: ${this.binName} v:${stringResponse(r)}`); + const binVersion = stringResponse(r); + log(`bin name: ${this.binName} v:${binVersion}`); - const baud = new Uint8Array(5) - baud[0] = 0x82 // set baud - pxt.HF2.write32(baud, 1, 115200) - await this.dapCmd(baud) - // setting the baud rate on serial may reset NRF (depending on daplink version), so delay after - await Promise.delay(200); + pxt.tickEvent("hid.flash.connect", { codal: this.usesCODAL ? 1 : 0, daplink: daplinkVersion, bin: binVersion }); + if (setBaudRateOnConnection) + await this.setBaudRate() // only init after setting baud rate, in case we got reset await this.cortexM.init() + if (resetOnConnection) { + log(`reset cortex`) + await this.cortexM.reset(true) + } - const res = await this.readWords(0x10000010, 2); - this.pageSize = res[0] - this.numPages = res[1] - log(`page size ${this.pageSize}, num pages ${this.numPages}`); - + await this.readPageSize() + // jacdac needs to run to set the xchg address await this.checkStateAsync(true); - await this.jacdacSetup(); + await this.initJacdac(connectionId) + + this.initialized = true + this.io.onConnectionChanged() + // start jacdac, serial async + this.startReadSerial(connectionId) + } + + private async clearCommandsAsync() { + try { + await pxt.Util.promiseTimeout(CONNECTION_CHECK_TIMEOUT, (async () => { + // before calling into dapjs, push through a few commands to make sure the responses + // to commands from previous sessions (if any) are flushed. Count of 5 is arbitrary. + for (let i = 0; i < 5; i++) { + try { + await this.getDaplinkVersionAsync(); + } catch (e) { } + } + })()); + } catch (e) { + const errOut = new Error(e); + (errOut as any).type = "inittimeout"; + throw errOut; + } + } - this.startReadSerial(); + private async getDaplinkVersionAsync() { + return await this.dapCmdNums(0x00, 0x04); } private async checkStateAsync(resume?: boolean): Promise { @@ -206,6 +382,7 @@ class DAPWrapper implements pxt.packetio.PacketIOWrapper { await this.cortexM.resume(); } catch (e) { log(`cortex state failed`) + pxt.tickEvent("hid.checkstate.error") console.debug(e) } } @@ -215,193 +392,227 @@ class DAPWrapper implements pxt.packetio.PacketIOWrapper { throw new Error(lf("Download cancelled")); } - disconnectAsync() { + async disconnectAsync() { log(`disconnect`) this.flashAborted = true; - return this.stopSerialAsync() - .then(() => this.io.disconnectAsync()); + this.initialized = false; + await this.stopReadersAsync(); + await this.io.disconnectAsync(); } - reflashAsync(resp: pxtc.CompileResult): Promise { + async reflashAsync( + resp: pxtc.CompileResult, + progressCallback?: (percentageComplete: number) => void + ): Promise { + pxt.tickEvent("hid.flash.start"); + log("reflash") startTime = 0 - pxt.tickEvent("hid.flash.start"); + // JACDAC_WEBUSB is defined in microsoft/pxt-jacdac/pxt.json + const codalJson = resp.outfiles["codal.json"] + this.jacdacInHex = codalJson && !!pxt.Util.jsonTryParse(codalJson)?.definitions?.JACDAC_WEBUSB; this.flashAborted = false; - this.flashing = true; - return (this.io.isConnected() ? Promise.resolve() : this.io.reconnectAsync()) - .then(() => this.stopSerialAsync()) - .then(() => this.cortexM.init()) - .then(() => this.cortexM.reset(true)) - .then(() => this.checkStateAsync()) - .then(() => this.readUICR()) - .then(uicr => { - // shortcut, do a full flash - if (uicr != 0 || this.forceFullFlash) { - pxt.tickEvent("hid.flash.uicrfail"); - return this.fullVendorCommandFlashAsync(resp); - } - // check flash checksums - return this.computeFlashChecksum(resp) - .then(chk => { - // let's do a quick flash! - if (chk.quick) - return this.quickHidFlashAsync(chk.changed); - else - return this.fullVendorCommandFlashAsync(resp); - }); - }) - .then(() => this.checkStateAsync(true)) - .finally(() => { this.flashing = false }) + if (!this.io.isConnected()) { + await this.io.reconnectAsync(); + } + + await this.stopReadersAsync(); + await this.clearCommandsAsync() + await this.cortexM.init(); + await this.cortexM.reset(true); + await this.checkStateAsync(); + const uicr = await this.readUICR(); + + pxt.tickEvent("hid.flash.uicr", { uicr }); + // shortcut, do a full flash + if (uicr != 0 || this.forceFullFlash) { + pxt.tickEvent("hid.flash.uicrfail"); + await this.fullVendorCommandFlashAsync(resp, progressCallback); + } else { + // check flash checksums + const chk = await this.computeFlashChecksum(resp); + pxt.tickEvent("hid.flash.checksum", { quick: chk.quick ? 1 : 0, changed: chk.changed ? chk.changed.length : 0 }); + if (chk.quick) { + // let's do a quick flash! + await this.quickHidFlashAsync(chk.changed, progressCallback); + } else { + await this.fullVendorCommandFlashAsync(resp, progressCallback); + } + } + + await this.checkStateAsync(true); + pxt.tickEvent("hid.flash.success"); // don't disconnect here // the micro:bit will automatically disconnect and reconnect // via the webusb events } - private recvPacketAsync() { + private recvPacketAsync(timeout?: number) { if (this.io.recvPacketAsync) - return this.io.recvPacketAsync() + return this.io.recvPacketAsync(timeout); else - return this.pbuf.shiftAsync() - } - - private dapCmd(buf: Uint8Array) { - return this.io.sendPacketAsync(buf) - .then(() => this.recvPacketAsync()) - .then(resp => { - if (resp[0] != buf[0]) { - const msg = `bad dapCmd response: ${buf[0]} -> ${resp[0]}` - // in case we got an invalid response, try to get another response, in case the current - // response is a left-over from previous communications - log(msg + "; retrying") - return this.recvPacketAsync() - .then(resp => { - if (resp[0] == buf[0]) - return resp - throw new Error(msg) - }, err => { - throw new Error(msg) - }) + return this.pbuf.shiftAsync(timeout); + } + + private async dapCmd(buf: Uint8Array) { + await this.io.sendPacketAsync(buf); + const resp = await this.recvPacketAsync(); + if (resp[0] != buf[0]) { + pxt.tickEvent('hid.flash.cmderror', { req: buf[0], resp: resp[0] }); + const msg = `bad dapCmd response: ${buf[0]} -> ${resp[0]}` + + // in case we got an invalid response, try to get another response, in case the current + // response is a left-over from previous communications + log(msg + "; retrying"); + try { + // Add in a timeout, as this can stall if device thinks communication is complete. + const secondTryResp = await this.recvPacketAsync(RETRY_DAP_CMD_TIMEOUT); + if (secondTryResp[0] === buf[0]) { + log(msg + "; retry success"); + return secondTryResp; } - return resp - }) + } catch (e) { + pxt.tickEvent('hid.flash.cmderror.retryfailed', { req: buf[0], resp: resp[0] }); + log(e); + } + throw new Error(`retry failed ${msg}`); + } + return resp; } private dapCmdNums(...nums: number[]) { return this.dapCmd(new Uint8Array(nums)) } - private fullVendorCommandFlashAsync(resp: pxtc.CompileResult): Promise { + private async fullVendorCommandFlashAsync( + resp: pxtc.CompileResult, + progressCallback?: (percentageComplete: number) => void + ): Promise { log("full flash") + pxt.tickEvent("hid.flash.full.start"); + const start = Date.now(); const chunkSize = 62; let sentPages = 0; - return Promise.resolve() - .then(() => this.dapCmdNums(0x8A /* DAPLinkFlash.OPEN */, 1)) - .then((res) => { - log(`daplinkflash open: ${pxt.U.toHex(res)}`) - if (res[1] !== 0) - throw new Error(lf("Download failed, please try again")); - const binFile = resp.outfiles[this.binName]; - log(`bin file ${this.binName} in ${Object.keys(resp.outfiles).join(', ')}, ${binFile?.length || -1}b`) - const hexUint8 = pxt.U.stringToUint8Array(binFile); - log(`hex ${hexUint8?.byteLength || -1}b, ~${(hexUint8.byteLength / chunkSize) | 0} chunks of ${chunkSize}b`) - - const sendPages = (offset: number = 0): Promise => { - const end = Math.min(hexUint8.length, offset + chunkSize); - const nextPageData = hexUint8.slice(offset, end); - const cmdData = new Uint8Array(2 + nextPageData.length) - cmdData[0] = 0x8C /* DAPLinkFlash.WRITE */ - cmdData[1] = nextPageData.length - cmdData.set(nextPageData, 2) - if (sentPages % 128 == 0) // reduce logging - log(`next page ${sentPages}: [${offset.toString(16)}, ${end.toString(16)}] (${Math.ceil((hexUint8.length - end) / 1000)}kb left)`) - return this.dapCmd(cmdData) - .then(() => { - this.checkAborted() - if (end < hexUint8.length) { - sentPages++; - return sendPages(end); - } - return Promise.resolve() - }); - } + try { + await pxt.Util.promiseTimeout( + FULL_FLASH_TIMEOUT, + (async () => { + const dapOpenRes = await this.dapCmdNums(0x8A /* DAPLinkFlash.OPEN */, 1); + log(`daplinkflash open: ${pxt.U.toHex(dapOpenRes)}`); + if (dapOpenRes[1] !== 0) { + pxt.tickEvent('hid.flash.full.error.open', { res: dapOpenRes[1] }); + throw new Error(lf("Download failed, please try again")); + } + const binFile = this.getBinFile(resp); + log(`bin file ${this.binName} in ${Object.keys(resp.outfiles).join(', ')}, ${binFile?.length || -1}b`); + const hexUint8 = pxt.U.stringToUint8Array(binFile); + log(`hex ${hexUint8?.byteLength || -1}b, ~${(hexUint8.byteLength / chunkSize) | 0} chunks of ${chunkSize}b`); + + let offset = 0; + + while (offset < hexUint8.length) { + const end = Math.min(hexUint8.length, offset + chunkSize); + const nextPageData = hexUint8.slice(offset, end); + const cmdData = new Uint8Array(2 + nextPageData.length); + cmdData[0] = 0x8C; /* DAPLinkFlash.WRITE */ + cmdData[1] = nextPageData.length; + cmdData.set(nextPageData, 2); + if (sentPages % 128 == 0) { // reduce logging + progressCallback(offset / hexUint8.length); + log(`next page ${sentPages}: [${offset.toString(16)}, ${end.toString(16)}] (${Math.ceil((hexUint8.length - end) / 1000)}kb left)`); + } + await this.dapCmd(cmdData); + this.checkAborted(); + sentPages++; + offset = end; + } - return sendPages(); - }) - .then(() => { - log(`close`) - return this.dapCmdNums(0x8B /* DAPLinkFlash.CLOSE */); - }) - .then(res => { - log(`daplinkclose: ${pxt.U.toHex(res)}`) - return this.dapCmdNums(0x89 /* DAPLinkFlash.RESET */); - }) - .then((res) => { - log(`daplinkreset: ${pxt.U.toHex(res)}`) - log(`full flash done`); - }) - .timeout(FULL_FLASH_TIMEOUT, timeoutMessage) - .catch((e) => { - log(`error: abort`) - this.flashAborted = true; - return this.resetAndThrowAsync(e); - }); + log("close"); + const closeRes = await this.dapCmdNums(0x8B /* DAPLinkFlash.CLOSE */); + log(`daplinkclose: ${pxt.U.toHex(closeRes)}`); + const resetRes = await this.dapCmdNums(0x89 /* DAPLinkFlash.RESET */); + log(`daplinkreset: ${pxt.U.toHex(resetRes)}`); + log(`full flash done after ${Date.now() - start}ms`); + pxt.tickEvent("hid.flash.full.success"); + })(), + timeoutMessage + ) + } catch (e) { + log(`error: abort`); + pxt.tickEvent("hid.flash.full.error"); + this.flashAborted = true; + return this.resetAndThrowAsync(e); + }; } - private resetAndThrowAsync(e: any) { - log(`reset on error`) - console.debug(e) + private async resetAndThrowAsync(e: any) { + log(`reset on error`); + pxt.tickEvent("hid.flash.reset"); + console.debug(e); // reset any pending daplink - return this.dapCmdNums(0x89 /* DAPLinkFlash.RESET */) - .catch((e2: any) => { - // Best effort reset, no-op if there's an error - }) - .then(() => this.cortexM.reset(false)) - .catch((e2: any) => { - // Best effort reset, no-op if there's an error - }) - .then(() => { - throw e; - }); - } - - private readUICR() { - return this.readWords(0x10001014, 1) - .then(v => { - const uicr = v[0] & 0xff; - log(`uicr: ${uicr.toString(16)} (${v[0].toString(16)})`); - return uicr; - }); - } - - private computeFlashChecksum(resp: pxtc.CompileResult) { - const binFile = resp.outfiles[this.binName]; - if (!binFile) - throw new Error(`unable to find ${this.binName} in outfiles ${Object.keys(resp.outfiles).join(', ')}`); - - return this.getFlashChecksumsAsync() - .then(checksums => { - log(`checksums ${pxt.Util.toHex(checksums)}`); - // TODO this is seriously inefficient (130ms on a fast machine) - const uf2 = ts.pxtc.UF2.newBlockFile(); - ts.pxtc.UF2.writeHex(uf2, binFile.split(/\r?\n/)); - const bytes = pxt.U.stringToUint8Array(ts.pxtc.UF2.serializeFile(uf2)); - const parsed = ts.pxtc.UF2.parseFile(bytes); - - const aligned = DAPWrapper.pageAlignBlocks(parsed, this.pageSize); - const changed = DAPWrapper.onlyChanged(aligned, checksums, this.pageSize); - const quick = changed.length < aligned.length / 2; - log(`pages: ${aligned.length}, changed ${changed.length}, ${quick ? "quick" : "full"}`); - return { - quick, - changed - } - }); + try { + await this.dapCmdNums(0x89 /* DAPLinkFlash.RESET */); + } catch (e) { + // Best effort reset, no-op if there's an error + } + try { + await this.cortexM.reset(false); + } catch (e) { + // Best effort reset, no-op if there's an error + } + throw e; + } + + private async readUICR() { + const v = await this.readWords(0x10001014, 1); + const uicr = v[0] & 0xff; + log(`uicr: ${uicr.toString(16)} (${v[0].toString(16)})`); + return uicr; + } + + private getBinFile(resp: pxtc.CompileResult) { + const multiVariantBinFile = resp.outfiles[this.binName]; + if (multiVariantBinFile) + return multiVariantBinFile; + + const dvBin = resp.builtVariants?.find(el => el === this.devVariant) && resp.outfiles[pxtc.BINARY_HEX]; + if (dvBin) + return resp.outfiles[pxtc.BINARY_HEX]; + + throw new Error(`unable to find ${this.binName} in outfiles ${Object.keys(resp.outfiles).join(', ')}`); + } + + private async computeFlashChecksum(resp: pxtc.CompileResult) { + const binFile = this.getBinFile(resp); + + const checksums = await this.getFlashChecksumsAsync(); + log(`checksums ${pxt.Util.toHex(checksums)}`); + // TODO this is seriously inefficient (130ms on a fast machine) + const uf2 = ts.pxtc.UF2.newBlockFile(); + ts.pxtc.UF2.writeHex(uf2, binFile.split(/\r?\n/)); + const bytes = pxt.U.stringToUint8Array(ts.pxtc.UF2.serializeFile(uf2)); + const parsed = ts.pxtc.UF2.parseFile(bytes); + + const aligned = DAPWrapper.pageAlignBlocks(parsed, this.pageSize); + const changed = DAPWrapper.onlyChanged(aligned, checksums, this.pageSize); + const quick = changed.length < aligned.length / 2; + log(`pages: ${aligned.length}, changed ${changed.length}, ${quick ? "quick" : "full"}`); + return { + quick, + changed + } } - private quickHidFlashAsync(changed: ts.pxtc.UF2.Block[]): Promise { + private async quickHidFlashAsync( + changed: ts.pxtc.UF2.Block[], + progressCallback?: (percentageComplete: number) => void + ): Promise { log("quick flash") - const runFlash = (b: ts.pxtc.UF2.Block, dataAddr: number) => { + pxt.tickEvent("hid.flash.quick.start"); + + const start = Date.now(); + const runFlash = async (b: ts.pxtc.UF2.Block, dataAddr: number) => { const cmd = this.cortexM.prepareCommand(); cmd.halt(); @@ -414,95 +625,102 @@ class DAPWrapper implements pxt.packetio.PacketIOWrapper { cmd.writeCoreRegister(1, dataAddr); cmd.writeCoreRegister(2, this.pageSize >> 2); - return Promise.resolve() - .then(() => { - logV("setregs") - return cmd.go() - }) - .then(() => { - // starts the program - logV(`cortex.debug.enable`) - return this.cortexM.debug.enable() - }) + logV("setregs"); + await cmd.go(); + // starts the program + logV(`cortex.debug.enable`); + return this.cortexM.debug.enable(); } - return Promise.resolve() - .then(() => this.cortexM.memory.writeBlock(loadAddr, flashPageBIN)) - .then(() => Promise.mapSeries(pxt.U.range(changed.length), - i => { - this.checkAborted(); - let b = changed[i]; - if (b.targetAddr >= 0x10000000) { - log(`target address ${b.targetAddr.toString(16)} > 0x10000000`) - return Promise.resolve(); - } + const quickHidFlashCoreAsync = async () => { + await this.cortexM.memory.writeBlock(loadAddr, flashPageBIN); + for (let i = 0; i < changed.length; i++) { + this.checkAborted(); + let b = changed[i]; + if (b.targetAddr >= 0x10000000) { + log(`target address 0x${b.targetAddr.toString(16)} > 0x10000000`); + continue; + } - log("about to write at 0x" + b.targetAddr.toString(16)); + log(`about to write at 0x${b.targetAddr.toString(16)}`); + progressCallback(i / changed.length); - let writeBl = Promise.resolve(); + const thisAddr = (i & 1) ? dataAddr : dataAddr + this.pageSize; + const nextAddr = (i & 1) ? dataAddr + this.pageSize : dataAddr; - let thisAddr = (i & 1) ? dataAddr : dataAddr + this.pageSize; - let nextAddr = (i & 1) ? dataAddr + this.pageSize : dataAddr; + if (i == 0) { + const u32data = new Uint32Array(b.data.length / 4); + for (let i = 0; i < b.data.length; i += 4) + u32data[i >> 2] = pxt.HF2.read32(b.data, i); + await this.cortexM.memory.writeBlock(thisAddr, u32data); + } - if (i == 0) { - let u32data = new Uint32Array(b.data.length / 4); - for (let i = 0; i < b.data.length; i += 4) - u32data[i >> 2] = pxt.HF2.read32(b.data, i); - writeBl = this.cortexM.memory.writeBlock(thisAddr, u32data); - } + await runFlash(b, thisAddr); + const next = changed[i + 1]; + if (next) { + logV("write next"); + const buf = new Uint32Array(next.data.buffer); + await this.cortexM.memory.writeBlock(nextAddr, buf); + } + logV("wait"); + await this.cortexM.waitForHalt(500); + logV("done block"); + } - return writeBl - .then(() => runFlash(b, thisAddr)) - .then(() => { - let next = changed[i + 1]; - if (!next) - return Promise.resolve(); - logV("write next"); - let buf = new Uint32Array(next.data.buffer); - return this.cortexM.memory.writeBlock(nextAddr, buf); - }) - .then(() => { - logV("wait"); - return this.cortexM.waitForHalt(500); - }) - .then(() => { - logV("done block"); - }); - })) - .then(() => { - log("quick flash done"); - pxt.tickEvent("hid.flash.done"); - return this.cortexM.reset(false); - }) - .then(() => this.checkStateAsync(true)) - .timeout(PARTIAL_FLASH_TIMEOUT, timeoutMessage) - .catch((e) => { - this.flashAborted = true; - return this.resetAndThrowAsync(e); - }); - } + log(`quick flash done after ${Date.now() - start}ms`); + await this.cortexM.reset(false); + pxt.tickEvent("hid.flash.quick.success"); + await this.checkStateAsync(true); + } - private getFlashChecksumsAsync() { - log("flash checksums") - let pages = this.numPages - return this.cortexM.runCode(computeChecksums2, loadAddr, loadAddr + 1, 0xffffffff, stackAddr, true, - dataAddr, 0, this.pageSize, pages) - .then(() => this.cortexM.memory.readBlock(dataAddr, pages * 2, this.pageSize)) + try { + await pxt.Util.promiseTimeout( + PARTIAL_FLASH_TIMEOUT, + quickHidFlashCoreAsync(), + timeoutMessage + ); + } catch (e) { + pxt.tickEvent("hid.flash.quick.error"); + this.flashAborted = true; + return this.resetAndThrowAsync(e); + } } - private readWords(addr: number, numWords: number) { - return this.cortexM.memory.readBlock(addr, numWords, this.pageSize) - // assume browser is little-endian - .then(u8 => new Uint32Array(u8.buffer)) + private async getFlashChecksumsAsync() { + log("flash checksums"); + let pages = this.numPages; + await this.cortexM.runCode( + computeChecksums2, + loadAddr, + loadAddr + 1, + 0xffffffff, + stackAddr, + true, + dataAddr, + 0, + this.pageSize, + pages + ); + return this.cortexM.memory.readBlock( + dataAddr, + pages * 2, + this.pageSize + ); + } + + private async readWords(addr: number, numWords: number) { + const u8 = await this.cortexM.memory.readBlock(addr, numWords, this.pageSize); + // assume browser is little-endian + return new Uint32Array(u8.buffer); } private writeWords(addr: number, buf: Uint32Array) { return this.cortexM.memory.writeBlock(addr, buf) } - private readBytes(addr: number, numBytes: number) { - return this.cortexM.memory.readBlock(addr, (numBytes + 3) >> 2, this.pageSize) - .then(u8 => u8.length == numBytes ? u8 : u8.slice(0, numBytes)) + private async readBytes(addr: number, numBytes: number) { + const u8 = await this.cortexM.memory.readBlock(addr, (numBytes + 3) >> 2, this.pageSize); + return u8.length == numBytes ? u8 : u8.slice(0, numBytes); } static onlyChanged(blocks: ts.pxtc.UF2.Block[], checksums: Uint8Array, pageSize: number) { @@ -580,74 +798,92 @@ class DAPWrapper implements pxt.packetio.PacketIOWrapper { return this.cortexM.memory.write32(addr, val) } - private async findJacdacXchgAddr() { + private async findJacdacXchgAddr(cid: number): Promise { const memStart = 0x2000_0000 const memStop = memStart + 128 * 1024 - const checkSize = 1024 - - let p0 = 0x20006000 - let p1 = 0x20006000 + checkSize - - const check = async (addr: number) => { - if (addr < memStart) - return null - if (addr + checkSize > memStop) - return null - const buf = await this.readWords(addr, checkSize >> 2) - for (let i = 0; i < buf.length; ++i) { - if (buf[i] == 0x786D444A && buf[i + 1] == 0xB0A6C0E9) - return addr + (i << 2) - } - return 0 - } - - while (true) { - const a0 = await check(p0) - if (a0) return a0 - const a1 = await check(p1) - if (a1) return a1 - if (a0 === null && a1 === null) - return null - p0 -= checkSize - p1 += checkSize + const addr = (await this.readWords(memStop - 4, 1))[0] + if (cid != this.connectionId) return null + if (memStart <= addr && addr < memStop) { + const buf = await this.readWords(addr, 2) + if (buf[0] == 0x786D444A && buf[1] == 0xB0A6C0E9) + return addr } + return null } - private async jacdacSetup() { + /** + * Sniff Jacdac exchange address + * @returns + */ + private async initJacdac(connectionId: number) { this.xchgAddr = null - if (!this.useJACDAC) - return - await Promise.delay(700); // wait for the program to start and setup memory correctly - const xchg = await this.findJacdacXchgAddr() - if (xchg == null) + this.irqn = undefined + this.lastXchg = undefined + if (!this.usesCODAL) { + log(`jacdac: CODAL disabled`) return - const info = await this.readBytes(xchg, 16) - this.irqn = info[8] - if (info[12 + 2] != 0xff) { - console.error("invalid memory; try power-cycling the micro:bit") + } + if (this.jacdacInHex === false) { + log(`jacdac: jacdac not compiled in`) return } - this.xchgAddr = xchg - // clear initial lock - await this.writeWord(xchg + 12, 0) - log(`jacdac exchange address: 0x${xchg.toString(16)}; irqn=${this.irqn}`) - } - private async triggerIRQ(irqn: number) { - const addr = 0xE000E200 + (irqn >> 5) * 4 - await this.writeWord(addr, 1 << (irqn & 31)) - } + try { + // allow jacdac to boot + const now = pxt.U.now() + await pxt.Util.delay(1000) + let xchgRetry = 0 + let xchg: number + while (xchg == null && xchgRetry++ < 3) { + log(`jacdac: finding xchg address (retry ${xchgRetry})`) + if (xchgRetry > 0) + await pxt.Util.delay(500); // wait for the program to start and setup memory correctly + if (connectionId != this.connectionId) return; + xchg = await this.findJacdacXchgAddr(connectionId) + } + log(`jacdac: exchange address 0x${xchg ? xchg.toString(16) : "?"}; ${xchgRetry} retries; ${(pxt.U.now() - now) | 0}ms`) + if (xchg == null) { + log("jacdac: xchg address not found") + this.jacdacInHex = false + pxt.tickEvent("hid.flash.jacdac.error.missingxchg"); + return + } - private async jacdacProcess(hadSerial: boolean) { - if (this.xchgAddr == null) { - if (!hadSerial) - await this.dapDelay(5000) - return + if (connectionId != this.connectionId) return; + const info = await this.readBytes(xchg, 16) + if (info[12 + 2] != 0xff) { + log("jacdac: invalid memory; try power-cycling the micro:bit") + pxt.tickEvent("hid.flash.jacdac.error.invalidmemory"); + console.debug({ info, xchg }) + return + } + + // make sure connection is not outdated + if (connectionId != this.connectionId) return; + // clear initial lock + await this.writeWord(xchg + 12, 0) + // allow serial thread to use jacdac + this.irqn = info[8] + this.xchgAddr = xchg + log(`jacdac: exchange address 0x${this.xchgAddr.toString(16)}; irqn=${this.irqn}`) + pxt.tickEvent("hid.flash.jacdac.connected"); + } catch (e) { + if (connectionId != this.connectionId) { + log(`jacdac: setup aborted`) + return; + } else throw e } + } + private async triggerIRQ() { + const addr = 0xE000E200 + (this.irqn >> 5) * 4 + await this.writeWord(addr, 1 << (this.irqn & 31)) + } + + private async jacdacProcess(): Promise { const now = Date.now() if (this.lastXchg && now - this.lastXchg > 50) { - log("slow xchg: " + (now - this.lastXchg) + "ms") + logV("slow xchg: " + (now - this.lastXchg) + "ms") } this.lastXchg = now @@ -656,7 +892,7 @@ class DAPWrapper implements pxt.packetio.PacketIOWrapper { let inp = await this.readBytes(this.xchgAddr + 12, 256) if (inp[2]) { await this.writeWord(this.xchgAddr + 12, 0) - await this.triggerIRQ(this.irqn) + await this.triggerIRQ() inp = inp.slice(0, inp[2] + 12) this.onCustomEvent("jacdac", inp) numev++ @@ -685,7 +921,7 @@ class DAPWrapper implements pxt.packetio.PacketIOWrapper { await this.writeWords(this.xchgAddr + 12 + 256 + 4, new Uint32Array(bbody.buffer)) const bhead = this.currSend.buf.slice(0, 4) await this.writeWords(this.xchgAddr + 12 + 256, new Uint32Array(bhead.buffer)) - await this.triggerIRQ(this.irqn) + await this.triggerIRQ() this.lastSend = Date.now() numev++ } else { @@ -699,16 +935,7 @@ class DAPWrapper implements pxt.packetio.PacketIOWrapper { } } - if (numev == 0 && !hadSerial) - await this.dapDelay(5000) - } - - private dapDelay(micros: number) { - if (micros > 0xffff) - throw new Error("too large delay") - const cmd = new Uint8Array([0x09, 0, 0]) - pxt.HF2.write16(cmd, 1, micros) - return this.dapCmd(cmd) + return numev } } diff --git a/editor/patch.ts b/editor/patch.ts index f1474a115ff..c2618dc9df9 100644 --- a/editor/patch.ts +++ b/editor/patch.ts @@ -1,108 +1,250 @@ -/** - * - FALSE - FALSE - FALSE - FALSE - FALSE - FALSE - FALSE - FALSE - TRUE - FALSE - FALSE - FALSE - FALSE - FALSE - FALSE - FALSE - TRUE - FALSE - FALSE - FALSE - FALSE - FALSE - FALSE - FALSE - FALSE - - - to - - ` - # # # # # - . . . . # - . . . . . - . . . . # - . . . . # - ` - - - */ - export function patchBlocks(pkgTargetVersion: string, dom: Element) { + + if (pxt.semver.majorCmp(pkgTargetVersion || "0.0.0", "7.0.13") <= 0) { + // Variable pin param + /* + + DigitalPin.P0 + + + converts to + + + + + DigitalPin.P0 + + + + */ + pxt.U.toArray(dom.querySelectorAll("block[type=device_get_digital_pin]")) + .concat(pxt.U.toArray(dom.querySelectorAll("shadow[type=device_get_digital_pin]"))) + .concat(pxt.U.toArray(dom.querySelectorAll("block[type=device_set_digital_pin]"))) + .concat(pxt.U.toArray(dom.querySelectorAll("block[type=device_get_analog_pin]"))) + .concat(pxt.U.toArray(dom.querySelectorAll("shadow[type=device_get_analog_pin]"))) + .concat(pxt.U.toArray(dom.querySelectorAll("block[type=device_set_analog_pin]"))) + .concat(pxt.U.toArray(dom.querySelectorAll("block[type=device_set_analog_period]"))) + .concat(pxt.U.toArray(dom.querySelectorAll("block[type=pins_pulse_in]"))) + .concat(pxt.U.toArray(dom.querySelectorAll("shadow[type=pins_pulse_in]"))) + .concat(pxt.U.toArray(dom.querySelectorAll("block[type=device_set_servo_pin]"))) + .concat(pxt.U.toArray(dom.querySelectorAll("block[type=device_set_servo_pulse]"))) + .concat(pxt.U.toArray(dom.querySelectorAll("block[type=device_analog_set_pitch_pin]"))) + .concat(pxt.U.toArray(dom.querySelectorAll("block[type=device_set_pull]"))) + .concat(pxt.U.toArray(dom.querySelectorAll("block[type=device_set_pin_events]"))) + .concat(pxt.U.toArray(dom.querySelectorAll("block[type=pin_neopixel_matrix_width]"))) + .concat(pxt.U.toArray(dom.querySelectorAll("block[type=spi_pins]"))) + .concat(pxt.U.toArray(dom.querySelectorAll("block[type=pin_set_audio_pin]"))) + .forEach(node => { + const blockType = node.getAttribute("type"); + pxt.U.toArray(node.children) + .filter(oldPinNode => { + if (oldPinNode.tagName != "field") return false; + switch (blockType) { + case "device_get_digital_pin": + case "device_set_digital_pin": + case "device_get_analog_pin": + case "device_set_analog_pin": + case "pins_pulse_in": + case "device_set_servo_pin": + case "device_analog_set_pitch_pin": + case "pin_set_audio_pin": + return oldPinNode.getAttribute("name") === "name"; + case "device_set_analog_period": + case "device_set_pull": + case "device_set_pin_events": + case "pin_neopixel_matrix_width": + return oldPinNode.getAttribute("name") === "pin"; + case "device_set_servo_pulse": + return oldPinNode.getAttribute("name") === "value"; + case "spi_pins": + return ["mosi", "miso", "sck"].includes(oldPinNode.getAttribute("name")); + } + return false; + }) + .forEach(oldPinNode => { + const valueNode = node.ownerDocument.createElement("value"); + valueNode.setAttribute("name", oldPinNode.getAttribute("name")); + + let nodeText = oldPinNode.textContent; + const pinShadowNode = node.ownerDocument.createElement("shadow"); + const [enumName, pinName] = nodeText.split("."); + + let pinBlockType; + switch (enumName) { + case "DigitalPin": + pinBlockType = "digital_pin_shadow"; + break; + case "AnalogPin": + pinBlockType = "analog_pin_shadow"; + break; + } + if (!pinBlockType) return; + + // If this is one of the read/write pins, narrow to the read write shadow + if (blockType === "device_get_analog_pin") { + switch (pinName) { + case "P0": + case "P1": + case "P2": + case "P3": + case "P4": + case "P10": + pinBlockType = "analog_read_write_pin_shadow"; + nodeText = `AnalogReadWritePin.${pinName}`; + break; + } + } + + pinShadowNode.setAttribute("type", pinBlockType); + + const fieldNode = node.ownerDocument.createElement("field"); + fieldNode.setAttribute("name", "pin"); + fieldNode.textContent = nodeText; + + pinShadowNode.appendChild(fieldNode); + valueNode.appendChild(pinShadowNode); + node.replaceChild(valueNode, oldPinNode); + }); + }); + } + + if (pxt.semver.majorCmp(pkgTargetVersion || "0.0.0", "5.0.12") <= 0) { + // Eighth note misspelling + /* + + IconNames.EigthNote + + + converts to + + + IconNames.EighthNote + + */ + pxt.U.toArray(dom.querySelectorAll("block[type=basic_show_icon]>field[name=i]")) + .filter(node => node.textContent === "IconNames.EigthNote") + .forEach(node => node.textContent = "IconNames.EighthNote"); + + // Italian translation error + /* + + 466 + + + converts to + + + 466 + + */ + pxt.U.toArray(dom.querySelectorAll("shadow[type=device_note]>field[name=note]")) + .forEach(node => node.setAttribute("name", "name")); + } + // is this a old script? if (pxt.semver.majorCmp(pkgTargetVersion || "0.0.0", "1.0.0") >= 0) return; // showleds - const nodes = pxt.U.toArray(dom.querySelectorAll("block[type=device_show_leds]")) + /** + + FALSE + FALSE + FALSE + FALSE + FALSE + FALSE + FALSE + FALSE + TRUE + FALSE + FALSE + FALSE + FALSE + FALSE + FALSE + FALSE + TRUE + FALSE + FALSE + FALSE + FALSE + FALSE + FALSE + FALSE + FALSE + + + converts to + + + ` + . . . . . + . . . # . + . . . . . + . # . . . + . . . . . + ` + + + */ + pxt.U.toArray(dom.querySelectorAll("block[type=device_show_leds]")) .concat(pxt.U.toArray(dom.querySelectorAll("block[type=device_build_image]"))) .concat(pxt.U.toArray(dom.querySelectorAll("shadow[type=device_build_image]"))) .concat(pxt.U.toArray(dom.querySelectorAll("block[type=device_build_big_image]"))) - .concat(pxt.U.toArray(dom.querySelectorAll("shadow[type=device_build_big_image]"))); - nodes.forEach(node => { - // don't rewrite if already upgraded, eg. field LEDS already present - if (pxt.U.toArray(node.children).filter(child => child.tagName == "field" && "LEDS" == child.getAttribute("name"))[0]) - return; - // read LEDxx value and assmebly into a new field - const leds: string[][] = [[], [], [], [], []]; - pxt.U.toArray(node.children) - .filter(child => child.tagName == "field" && /^LED\d+$/.test(child.getAttribute("name"))) - .forEach(lednode => { - let n = lednode.getAttribute("name"); - let col = parseInt(n[3]); - let row = parseInt(n[4]); - leds[row][col] = lednode.innerHTML == "TRUE" ? "#" : "."; - // remove node - node.removeChild(lednode); - }); - // add new field - const f = node.ownerDocument.createElement("field"); - f.setAttribute("name", "LEDS"); - const s = '`\n' + leds.map(row => row.join('')).join('\n') + '\n`'; - f.appendChild(node.ownerDocument.createTextNode(s)); - node.insertBefore(f, null); - }); + .concat(pxt.U.toArray(dom.querySelectorAll("shadow[type=device_build_big_image]"))) + .forEach(node => { + // don't rewrite if already upgraded, eg. field LEDS already present + if (pxt.U.toArray(node.children).filter(child => child.tagName == "field" && "LEDS" == child.getAttribute("name"))[0]) + return; + // read LEDxx value and assmebly into a new field + const leds: string[][] = [[], [], [], [], []]; + pxt.U.toArray(node.children) + .filter(child => child.tagName == "field" && /^LED\d+$/.test(child.getAttribute("name"))) + .forEach(lednode => { + let n = lednode.getAttribute("name"); + let col = parseInt(n[3]); + let row = parseInt(n[4]); + leds[row][col] = lednode.innerHTML == "TRUE" ? "#" : "."; + // remove node + node.removeChild(lednode); + }); + // add new field + const f = node.ownerDocument.createElement("field"); + f.setAttribute("name", "LEDS"); + const s = '`\n' + leds.map(row => row.join('')).join('\n') + '\n`'; + f.appendChild(node.ownerDocument.createTextNode(s)); + node.insertBefore(f, null); + }); // radio /* - - -receivedNumber - - - -name -value - - - -receivedString - - -converts to - - -receivedNumber - - -name -value - - -receivedString - -*/ + + + receivedNumber + + + + name + value + + + + receivedString + + + converts to + + + receivedNumber + + + name + value + + + receivedString + + */ const varids: pxt.Map = {}; function addField(node: Element, renameMap: pxt.Map, name: string) { diff --git a/editor/tsconfig.json b/editor/tsconfig.json index 47c94f4a499..0390af30ae8 100644 --- a/editor/tsconfig.json +++ b/editor/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es5", + "target": "es2017", "noImplicitAny": true, "noImplicitReturns": true, "noImplicitThis": true, @@ -12,6 +12,14 @@ "rootDir": ".", "newLine": "LF", "sourceMap": false, - "jsx": "react" + "jsx": "react", + "lib": [ + "dom", + "dom.iterable", + "scripthost", + "es2017", + "ES2018.Promise" + ], + "types": [] } } diff --git a/fieldeditors/extensions.ts b/fieldeditors/extensions.ts index 94c4bf5bece..c7e4aae1bd9 100644 --- a/fieldeditors/extensions.ts +++ b/fieldeditors/extensions.ts @@ -1,15 +1,22 @@ -/// +/// /// import { FieldGestures } from "./field_gestures"; +import { FieldPinPicker } from "./field_pinPicker"; pxt.editor.initFieldExtensionsAsync = function (opts: pxt.editor.FieldExtensionOptions): Promise { pxt.debug('loading pxt-microbit field editors...') const res: pxt.editor.FieldExtensionResult = { - fieldEditors: [{ - selector: "gestures", - editor: FieldGestures - }] + fieldEditors: [ + { + selector: "gestures", + editor: FieldGestures + }, + { + selector: "pinpicker", + editor: FieldPinPicker + } + ] }; return Promise.resolve(res); } \ No newline at end of file diff --git a/fieldeditors/field_gestures.ts b/fieldeditors/field_gestures.ts index 0a4dbf12bd1..004063d8d67 100644 --- a/fieldeditors/field_gestures.ts +++ b/fieldeditors/field_gestures.ts @@ -1,32 +1,107 @@ -/// -/// -/// +/// -export interface FieldGesturesOptions extends pxtblockly.FieldImagesOptions { +const pxtblockly = pxt.blocks.requirePxtBlockly() +const Blockly = pxt.blocks.requireBlockly(); + +export interface FieldGesturesOptions { columns?: string; width?: string; } -export class FieldGestures extends pxtblockly.FieldImages implements Blockly.FieldCustom { +export class FieldGestures extends pxtblockly.FieldImages { public isFieldCustom_ = true; constructor(text: string, options: FieldGesturesOptions, validator?: Function) { - super(text, options, validator); - + super(text, options as any, validator); this.columns_ = parseInt(options.columns) || 4; this.width_ = parseInt(options.width) || 350; + this.addLabel_ = true; + } - this.renderSelectedImage_ = Blockly.FieldDropdown.prototype.renderSelectedText_; - this.updateSize_ = (Blockly.Field as any).prototype.updateSize_; + protected render_(): void { + if (this.addLabel_) { + this.renderSelectedText_() + this.positionBorderRect_(); + } + else { + super.render_(); + } } - trimOptions_() { + /** Renders the selected option, which must be text. */ + protected renderSelectedText_() { + // Retrieves the selected option to display through getText_. + this.getTextContent().nodeValue = this.getDisplayText_(); + const textElement = this.getTextElement(); + Blockly.utils.dom.addClass(textElement, 'blocklyDropdownText'); + textElement.setAttribute('text-anchor', 'start'); + + // Height and width include the border rect. + const hasBorder = !!this.borderRect_; + const height = Math.max( + hasBorder ? this.getConstants()!.FIELD_DROPDOWN_BORDER_RECT_HEIGHT : 0, + this.getConstants()!.FIELD_TEXT_HEIGHT, + ); + const textWidth = Blockly.utils.dom.getFastTextWidth( + this.getTextElement(), + this.getConstants()!.FIELD_TEXT_FONTSIZE, + this.getConstants()!.FIELD_TEXT_FONTWEIGHT, + this.getConstants()!.FIELD_TEXT_FONTFAMILY, + ); + const xPadding = hasBorder + ? this.getConstants()!.FIELD_BORDER_RECT_X_PADDING + : 0; + let arrowWidth = 0; + if (this.getSvgArrow()) { + arrowWidth = this.positionSVGArrow_( + textWidth + xPadding, + height / 2 - this.getConstants()!.FIELD_DROPDOWN_SVG_ARROW_SIZE / 2, + ); + } + this.size_.width = textWidth + arrowWidth + xPadding * 2; + this.size_.height = height; + + this.positionTextElement_(xPadding, textWidth); } - protected buttonClick_ = function (e: any) { - let value = e.target.getAttribute('data-value'); - this.setValue(value); - Blockly.DropDownDiv.hide(); - }; + positionSVGArrow_(x: number, y: number): number { + const svgArrow = this.getSvgArrow(); + if (!svgArrow) { + return 0; + } + const block = this.getSourceBlock(); + const hasBorder = !!this.borderRect_; + const xPadding = hasBorder + ? this.getConstants()!.FIELD_BORDER_RECT_X_PADDING + : 0; + const textPadding = this.getConstants()!.FIELD_DROPDOWN_SVG_ARROW_PADDING; + const svgArrowSize = this.getConstants()!.FIELD_DROPDOWN_SVG_ARROW_SIZE; + const arrowX = block.RTL ? xPadding : x + textPadding; + svgArrow.setAttribute( + 'transform', + 'translate(' + arrowX + ',' + y + ')', + ); + return svgArrowSize + textPadding; + } + + // This hack exists because svgArrow is private in Blockly's field dropdown. + // It should always be the last image element in the field group + protected getSvgArrow() { + if (this.fieldGroup_) { + const children = this.fieldGroup_.children; + + let lastImage: SVGImageElement; + + for (let i = 0; i < children.length; i++) { + if (children.item(i).tagName.toLowerCase() === "image") { + lastImage = children.item(i) as SVGImageElement; + } + } + + return lastImage; + } + + return undefined; + } } \ No newline at end of file diff --git a/fieldeditors/field_pinPicker.ts b/fieldeditors/field_pinPicker.ts new file mode 100644 index 00000000000..965200a23cf --- /dev/null +++ b/fieldeditors/field_pinPicker.ts @@ -0,0 +1,93 @@ +/// + +const pxtblockly = pxt.blocks.requirePxtBlockly() +const Blockly = pxt.blocks.requireBlockly(); + +const WARNING_ID = "pinpicker_warning"; + +export class FieldPinPicker extends pxtblockly.FieldGridPicker { + protected warningVisible: boolean; + + override init() { + super.init(); + + const sourceBlock = this.sourceBlock_; + if (sourceBlock.isShadow() || sourceBlock.isInFlyout) { + return; + } + + sourceBlock.workspace.addChangeListener(this.changeListener); + } + + private changeListener = (e: any) => { + if (e.type === Blockly.Events.BLOCK_MOVE && e.blockId === this.sourceBlock_.id) { + this.updateWarning(); + } + } + + protected override doValueUpdate_(newValue: string): void { + super.doValueUpdate_(newValue); + this.updateWarning(); + } + + protected updateWarning() { + this.hideWarning(); + const sourceBlock = this.sourceBlock_; + + if (!sourceBlock || !this.value_ || sourceBlock.isShadow() || sourceBlock.isInFlyout) { + return; + } + + const pin = this.value_.split(".")[1]; + + if (!isAnalogWriteOnlyPin(pin)) { + return; + } + + const parent = sourceBlock.outputConnection.targetBlock(); + + if (!parent || parent.type !== "device_get_analog_pin") { + return; + } + + this.showWarning(pin); + } + + protected showWarning(pin: string) { + if (!this.sourceBlock_) { + return; + } + this.sourceBlock_.setWarningText(pxt.U.lf("{0} is a write only analog pin", pin), WARNING_ID); + } + + protected hideWarning() { + if (!this.sourceBlock_) { + return; + } + this.sourceBlock_.setWarningText(null, WARNING_ID) + } + + override dispose(): void { + super.dispose(); + this.sourceBlock_?.workspace?.removeChangeListener(this.changeListener); + } +} + +function isAnalogWriteOnlyPin(pin: string) { + switch (pin) { + case "P5": + case "P6": + case "P7": + case "P8": + case "P9": + case "P11": + case "P12": + case "P13": + case "P14": + case "P15": + case "P16": + return true; + default: + return false; + } +} \ No newline at end of file diff --git a/fieldeditors/tsconfig.json b/fieldeditors/tsconfig.json index 9f5342cbf55..d033991474a 100644 --- a/fieldeditors/tsconfig.json +++ b/fieldeditors/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es5", + "target": "es2017", "noImplicitAny": false, "noImplicitReturns": true, "module": "commonjs", @@ -9,6 +9,7 @@ "newLine": "LF", "sourceMap": false, "allowSyntheticDefaultImports": true, - "declaration": true + "declaration": true, + "types": [] } } \ No newline at end of file diff --git a/libs/audio-recording/_locales/audio-recording-jsdoc-strings.json b/libs/audio-recording/_locales/audio-recording-jsdoc-strings.json new file mode 100644 index 00000000000..0aa81a0e00c --- /dev/null +++ b/libs/audio-recording/_locales/audio-recording-jsdoc-strings.json @@ -0,0 +1,20 @@ +{ + "record": "Functions to operate the v2 on-board microphone and speaker.", + "record.audioDuration": "Get how long the recorded audio clip is", + "record.audioIsPlaying": "Get whether the playback is active", + "record.audioIsRecording": "Get whether the microphone is listening", + "record.audioIsStopped": "Get whether the board is recording or playing back", + "record.audioStatus": "Test what the audio is doing", + "record.erase": "Clear the buffer", + "record.play": "Play the audio clip that is saved in the buffer", + "record.playAudio": "Play recorded audio", + "record.record": "Record an audio clip", + "record.setBothSamples": "Set the sample rate for both input and output", + "record.setInputSampleRate": "Change the sample rate of the splitter channel (audio input)", + "record.setMicGain": "Change how sensitive the microphone is. This changes the recording quality!", + "record.setMicrophoneGain": "Set sensitity of the microphone input", + "record.setOutputSampleRate": "Change the sample rate of the mixer channel (audio output)", + "record.setSampleRate": "Set the sample frequency for recording, playback, or both (default)\n* @param hz The sample frequency, in Hz", + "record.startRecording": "Record an audio clip for a maximum of 3 seconds", + "record.stop": "Stop recording" +} \ No newline at end of file diff --git a/libs/audio-recording/_locales/audio-recording-strings.json b/libs/audio-recording/_locales/audio-recording-strings.json new file mode 100644 index 00000000000..d910bff0c9b --- /dev/null +++ b/libs/audio-recording/_locales/audio-recording-strings.json @@ -0,0 +1,28 @@ +{ + "record.AudioEvent.StartedPlaying|block": "starts playing", + "record.AudioEvent.StartedRecording|block": "starts recording", + "record.AudioEvent.StoppedPlaying|block": "stops playing", + "record.AudioEvent.StoppedRecording|block": "stops recording", + "record.AudioLevels.High|block": "high", + "record.AudioLevels.Low|block": "low", + "record.AudioLevels.Medium|block": "medium", + "record.AudioRecordingMode.Playing|block": "playing", + "record.AudioRecordingMode.Recording|block": "recording", + "record.AudioRecordingMode.Stopped|block": "stopped", + "record.AudioSampleRateScope.Everything|block": "everything", + "record.AudioSampleRateScope.Playback|block": "playback", + "record.AudioSampleRateScope.Recording|block": "recording", + "record.AudioStatus.BufferEmpty|block": "empty", + "record.AudioStatus.Playing|block": "playing", + "record.AudioStatus.Recording|block": "recording", + "record.AudioStatus.Stopped|block": "stopped", + "record.BlockingState.Blocking|block": "until done", + "record.BlockingState.Nonblocking|block": "in background", + "record.audioStatus|block": "audio is $status", + "record.playAudio|block": "play audio clip $mode", + "record.setMicGain|block": "set microphone sensitivity to $gain", + "record.setSampleRate|block": "set sample rate to $hz || for $scope", + "record.startRecording|block": "record audio clip $mode", + "record|block": "Record", + "{id:category}Record": "Record" +} \ No newline at end of file diff --git a/libs/audio-recording/docs/reference/audio-recording.md b/libs/audio-recording/docs/reference/audio-recording.md new file mode 100644 index 00000000000..75b1606e513 --- /dev/null +++ b/libs/audio-recording/docs/reference/audio-recording.md @@ -0,0 +1,47 @@ +# Audio recording + +The **audio recording** extension lets you record and play back audio with the micro:bit. If your version of the micro:bit has a microphone you can record a brief amount of audio and play it back on the speaker or at a sound output pin. Audio that you record is stored in an audio [buffer](/types/buffer) and can be played later or recorded over with new audio. + +### ~ reminder + +#### Works with micro:bit V2 + +![works with micro:bit V2 only image](/static/v2/v2-only.png) + +Using these blocks requires the [micro:bit V2](/device/v2) hardware. If you use any blocks that attempt access flash memory on a micro:bit v1 board, you will see the **927** error code on the screen. + +### ~ + +## Blocks in this extension + +### Record and play + +```cards +record.startRecording(record.BlockingState.Blocking) +record.playAudio(record.BlockingState.Blocking) +``` + +### Settings + +```cards +record.setSampleRate(11000) +record.setMicGain(record.AudioLevels.Low) +``` + +### Status + +```cards +record.audioStatus(record.AudioStatus.Playing) +``` + +## See also + +[start recording](/reference/record/start-recording), +[play audio](/reference/record/play-audio), +[set sample rate](/reference/record/set-sample-rate), +[set mic gain](/reference/record/set-mic-gain), +[audio status](/reference/record/audio-status) + +```package +audio-recording +``` \ No newline at end of file diff --git a/libs/audio-recording/docs/reference/record.md b/libs/audio-recording/docs/reference/record.md new file mode 100644 index 00000000000..75b1606e513 --- /dev/null +++ b/libs/audio-recording/docs/reference/record.md @@ -0,0 +1,47 @@ +# Audio recording + +The **audio recording** extension lets you record and play back audio with the micro:bit. If your version of the micro:bit has a microphone you can record a brief amount of audio and play it back on the speaker or at a sound output pin. Audio that you record is stored in an audio [buffer](/types/buffer) and can be played later or recorded over with new audio. + +### ~ reminder + +#### Works with micro:bit V2 + +![works with micro:bit V2 only image](/static/v2/v2-only.png) + +Using these blocks requires the [micro:bit V2](/device/v2) hardware. If you use any blocks that attempt access flash memory on a micro:bit v1 board, you will see the **927** error code on the screen. + +### ~ + +## Blocks in this extension + +### Record and play + +```cards +record.startRecording(record.BlockingState.Blocking) +record.playAudio(record.BlockingState.Blocking) +``` + +### Settings + +```cards +record.setSampleRate(11000) +record.setMicGain(record.AudioLevels.Low) +``` + +### Status + +```cards +record.audioStatus(record.AudioStatus.Playing) +``` + +## See also + +[start recording](/reference/record/start-recording), +[play audio](/reference/record/play-audio), +[set sample rate](/reference/record/set-sample-rate), +[set mic gain](/reference/record/set-mic-gain), +[audio status](/reference/record/audio-status) + +```package +audio-recording +``` \ No newline at end of file diff --git a/libs/audio-recording/docs/reference/record/audio-status.md b/libs/audio-recording/docs/reference/record/audio-status.md new file mode 100644 index 00000000000..4cca1a5affe --- /dev/null +++ b/libs/audio-recording/docs/reference/record/audio-status.md @@ -0,0 +1,45 @@ +# audio Status + +Check to see if a certain audio status is true or not. + +```sig +record.audioStatus(record.AudioStatus.Playing) +``` + +The audio status is related to what operation is happening to the audio [buffer](/types/buffer) at the current moment. The audio buffer has several status conditions that you can check. It will return `true` or `false` to tell you if the audio is playing, recording, stopped, or the buffer has nothing in it. + +## Parameters + +* **status**: the audio status to check for. +>* `playing`: audio is currently playing. +>* `recording`: audio is currently recording. +>* `stopped`: audio playback is stopped. +>* `empty`: there is no audio recorded. + +## Returns + +* a [boolean](/types/boolean) value indicating whether the audio status type requested is either `true` or `false`. + +## Example + +Use buttons `A` and `B` to record and play audio. If no audio is recorded, skip the playback when button `B` is pressed. + +```blocks +input.onButtonPressed(Button.A, function () { + record.startRecording(record.BlockingState.Blocking) +}) +input.onButtonPressed(Button.B, function () { + if (!(record.audioStatus(record.AudioStatus.BufferEmpty))) { + record.playAudio(record.BlockingState.Blocking) + } +}) +``` + +## See also + +[start recording](/reference/record/start-recording), +[play audio](/reference/record/play-audio) + +```package +audio-recording +``` diff --git a/libs/audio-recording/docs/reference/record/play-audio.md b/libs/audio-recording/docs/reference/record/play-audio.md new file mode 100644 index 00000000000..bd92439e347 --- /dev/null +++ b/libs/audio-recording/docs/reference/record/play-audio.md @@ -0,0 +1,36 @@ +# play Audio + +Play the audio that was previously recorded in the audio buffer. + +```sig +record.playAudio(record.BlockingState.Blocking) +``` + +Any audio recorded in the audio buffer is played on the speaker or at the sound output pin. If there is nothing in the buffer, no sound is played. + +## Parameters + +* **mode**: the blocking state for audio playback. +>* `until done`: all of the audio is first played and then the program continues. +>* `in background`: the audio is played while the program continues. + +## Example + +Use the micro:bit as a sound recorder. Record sound when button `A` is pressed and play sound when button `B` is pressed. + +```blocks +input.onButtonPressed(Button.A, function () { + record.startRecording(record.BlockingState.Blocking) +}) +input.onButtonPressed(Button.B, function () { + record.playAudio(record.BlockingState.Blocking) +}) +``` +## See also + +[start recording](/reference/record/start-recording), +[set sample rate](/reference/record/set-sample-rate) + +```package +audio-recording +``` diff --git a/libs/audio-recording/docs/reference/record/set-mic-gain.md b/libs/audio-recording/docs/reference/record/set-mic-gain.md new file mode 100644 index 00000000000..aa7035fa66e --- /dev/null +++ b/libs/audio-recording/docs/reference/record/set-mic-gain.md @@ -0,0 +1,40 @@ +# set Mic Gain + +Set the sensitivity for the microphone to detect and record sounds. + +```sig +record.setMicGain(record.AudioLevels.Low) +``` + +The microphone will detect sounds at a certain loudness level. You can decide if you want to record only loud sounds or quieter sounds too by setting the microphone gain. + +Setting the microphone to `low` sensitivity will make the microphone pick up louder sounds. Setting the microphone to `high` sensitivity will make the microphone pick up all sorts of noise that you might not hear! + +## Parameters + +* **gain**: the sensitivity level for the microphone to detect and record sounds. +>* `low`: set the gain level to detect only loud sounds. +>* `medium` set the gain level to detect most sounds. +>* `high`: set the gain level to detect quiet and loud sounds. + +## Example + +Use buttons `A` and `B` to record and play audio. Set the microphone gain to `low` so that only loud sounds are recorded. + +```blocks +record.setMicGain(record.AudioLevels.Low) +input.onButtonPressed(Button.A, function () { + record.startRecording(record.BlockingState.Blocking) +}) +input.onButtonPressed(Button.B, function () { + record.playAudio(record.BlockingState.Blocking) +}) +``` + +## See also + +[start recording](/reference/record/start-recording) + +```package +audio-recording +``` diff --git a/libs/audio-recording/docs/reference/record/set-sample-rate.md b/libs/audio-recording/docs/reference/record/set-sample-rate.md new file mode 100644 index 00000000000..ab189b42c2d --- /dev/null +++ b/libs/audio-recording/docs/reference/record/set-sample-rate.md @@ -0,0 +1,43 @@ +# set Sample Rate + +Set the sample rate for audio recording and playback. + +```sig +record.setSampleRate(11000) +``` + +While recording, the sample rate determines how many audio "samples", or moments in time, of sound are recorded each second. If the sample rate is set to `1000`, then only 1000 moments of sound are recorded during a second. The higher the sample rate, the closer to the actual natural sound the playback will be. However, if a high sample rate is used, the audio buffer will have less duration of time for sound because more samples are used per second. + +When playing back, the sample rate sets the speed at which the sound is taken from the audio buffer and sent to the speaker or sound output pin. If the audio in the buffer was recorded at `11000` samples per second but the current sample rate is set at `22000`, the playback of the audio is twice as fast as it was recorded and it will sound different. The sound will play slower that the recorded sound if the playback sample rate is set to `550` before playing the audio back. + +## Parameters + +* **hz**: the [number](/types/number) of samples per second for recording or playback. +* **scope**: an optional operation scope for the sample rate. +>* `everything`: (default) set the same sample rate for both recording and playback. +>* `playback`: set the sample rate only for audio playback. +> * `recording`: set the sample rate only for audio recording. + +## Example + +Record audio at `22000` samples per second but play it back at `11000` samples per second. + +```blocks +record.setSampleRate(22000, record.AudioSampleRateScope.Recording) +record.setSampleRate(11000, record.AudioSampleRateScope.Playback) +input.onButtonPressed(Button.A, function () { + record.startRecording(record.BlockingState.Blocking) +}) +input.onButtonPressed(Button.B, function () { + record.playAudio(record.BlockingState.Blocking) +}) +``` + +## See also + +[start recording](/reference/record/start-recording), +[play audio](/reference/record/play-audio) + +```package +audio-recording +``` diff --git a/libs/audio-recording/docs/reference/record/start-recording.md b/libs/audio-recording/docs/reference/record/start-recording.md new file mode 100644 index 00000000000..7e545cd5610 --- /dev/null +++ b/libs/audio-recording/docs/reference/record/start-recording.md @@ -0,0 +1,39 @@ +# start Recording + +Begin recording sound in the audio buffer. + +```sig +record.startRecording(record.BlockingState.Blocking) +``` + +Audio recording starts and is recorded for a short period of time. Any previous audio is overwritten and the new audio takes its place. Audio is recorded on the micro:bit as a sequence of [numbers](/types/number) that represent sound and its loudness at a particular moment in time. This is called a sound "sample". The number of "samples" used to record sounds during one second is called the [sample rate](/reference/record/set-sample-rate). + +When audio is recorded, the audio buffer will contain enough samples that, when played back, will approximate natural sound waves as you listen to it. Any previous audio that was recorded is replaced with new audio. + +## Parameters + +* **mode**: the blocking state for the recording operation. +>* `until done`: the audio is recorded first and then the program continues. +>* `in background`: the audio is recorded while the program continues. + +## Example + +Use the micro:bit as a sound recorder. Record sound when button `A` is pressed and play sound when button `B` is pressed. + +```blocks +input.onButtonPressed(Button.A, function () { + record.startRecording(record.BlockingState.Blocking) +}) +input.onButtonPressed(Button.B, function () { + record.playAudio(record.BlockingState.Blocking) +}) +``` + +## See also + +[play audio](/reference/record/play-audio), +[set sample rate](/reference/record/set-sample-rate) + +```package +audio-recording +``` \ No newline at end of file diff --git a/libs/audio-recording/pxt.json b/libs/audio-recording/pxt.json new file mode 100644 index 00000000000..02ed3d60ed2 --- /dev/null +++ b/libs/audio-recording/pxt.json @@ -0,0 +1,14 @@ +{ + "name": "audio-recording", + "description": "Record sound clips. micro:bit (V2) only", + "dependencies": { + "core": "file:../core" + }, + "weight": 10, + "files": [ + "recording.ts", + "recording.cpp", + "shims.d.ts" + ], + "public": true +} \ No newline at end of file diff --git a/libs/audio-recording/recording.cpp b/libs/audio-recording/recording.cpp new file mode 100644 index 00000000000..b3c7ec56068 --- /dev/null +++ b/libs/audio-recording/recording.cpp @@ -0,0 +1,208 @@ +/* + The MIT License (MIT) + + Copyright (c) 2022 Lancaster University + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +#include "pxt.h" +#include "MicroBit.h" + +#if MICROBIT_CODAL +#include "StreamRecording.h" +#endif + +using namespace pxt; + +namespace record { + +#if MICROBIT_CODAL +static StreamRecording *recording = NULL; +static SplitterChannel *splitterChannel = NULL; +static MixerChannel *channel = NULL; +#endif + + +void checkEnv() { +#if MICROBIT_CODAL + if (recording == NULL) { + int defaultSampleRate = 11000; + MicroBitAudio::requestActivation(); + + splitterChannel = uBit.audio.splitter->createChannel(); + uBit.audio.mic->setSampleRate( defaultSampleRate ); + + recording = new StreamRecording(*splitterChannel); + + channel = uBit.audio.mixer.addChannel(*recording, defaultSampleRate); + + channel->setVolume(75.0); + } +#endif +} + +/** + * Record an audio clip + */ +//% promise +void record() { +#if MICROBIT_CODAL + checkEnv(); + recording->recordAsync(); +#else + target_panic(PANIC_VARIANT_NOT_SUPPORTED); +#endif +} + +/** + * Play the audio clip that is saved in the buffer + */ +//% +void play() { +#if MICROBIT_CODAL + checkEnv(); + recording->playAsync(); +#else + target_panic(PANIC_VARIANT_NOT_SUPPORTED); +#endif +} + +/** + * Stop recording + */ +//% +void stop() { +#if MICROBIT_CODAL + checkEnv(); + recording->stop(); +#else + target_panic(PANIC_VARIANT_NOT_SUPPORTED); +#endif +} + +/** + * Clear the buffer + */ +//% +void erase() { +#if MICROBIT_CODAL + checkEnv(); + recording->erase(); +#endif +} + +/** + * Set sensitity of the microphone input + */ +//% +void setMicrophoneGain(float gain) { +#if MICROBIT_CODAL + uBit.audio.processor->setGain(gain); +#endif +} + +/** + * Get how long the recorded audio clip is + */ +//% +int audioDuration(int sampleRate) { +#if MICROBIT_CODAL + return recording->duration(sampleRate); +#else + target_panic(PANIC_VARIANT_NOT_SUPPORTED); + return MICROBIT_NOT_SUPPORTED; +#endif +} + +/** + * Get whether the playback is active + */ +//% +bool audioIsPlaying() { +#if MICROBIT_CODAL + return recording->isPlaying(); +#else + return false; +#endif +} + +/** + * Get whether the microphone is listening + */ +//% +bool audioIsRecording() { +#if MICROBIT_CODAL + return recording->isRecording(); +#else + return false; +#endif +} + +/** + * Get whether the board is recording or playing back + */ +//% +bool audioIsStopped() { +#if MICROBIT_CODAL + return recording->isStopped(); +#else + return false; +#endif +} + +/** + * Change the sample rate of the splitter channel (audio input) + */ +//% +void setInputSampleRate(int sampleRate) { +#if MICROBIT_CODAL + checkEnv(); + uBit.audio.mic->setSampleRate(sampleRate); +#else + target_panic(PANIC_VARIANT_NOT_SUPPORTED); +#endif +} + + +/** + * Change the sample rate of the mixer channel (audio output) + */ +//% +void setOutputSampleRate(int sampleRate) { +#if MICROBIT_CODAL + checkEnv(); + channel->setSampleRate(sampleRate); +#else + target_panic(PANIC_VARIANT_NOT_SUPPORTED); +#endif +} + +/** + * Set the sample rate for both input and output +*/ +//% +void setBothSamples(int sampleRate) { +#if MICROBIT_CODAL + setOutputSampleRate(sampleRate); + uBit.audio.mic->setSampleRate(sampleRate); +#else + target_panic(PANIC_VARIANT_NOT_SUPPORTED); +#endif +} + +} // namespace record \ No newline at end of file diff --git a/libs/audio-recording/recording.ts b/libs/audio-recording/recording.ts new file mode 100644 index 00000000000..7426d655f59 --- /dev/null +++ b/libs/audio-recording/recording.ts @@ -0,0 +1,210 @@ +/* + The MIT License (MIT) + + Copyright (c) 2022 Lancaster University + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. +*/ + +/** + * Functions to operate the v2 on-board microphone and speaker. + */ +//% weight=5 color=#015f85 icon="\uf130" block="Record" advanced=false +namespace record { + // + + export enum AudioEvent { + //% block="starts playing" + StartedPlaying, + //% block="stops playing" + StoppedPlaying, + //% block="starts recording" + StartedRecording, + //% block="stops recording" + StoppedRecording + } + + export enum AudioLevels { + //% block="low" + Low = 1, + //% block="medium" + Medium, + //% block="high" + High + } + + export enum AudioSampleRateScope { + //% block="everything" + Everything, + //% block="playback" + Playback, + //% block="recording" + Recording + } + + export enum AudioRecordingMode { + //% block="stopped" + Stopped, + //% block="recording" + Recording, + //% block="playing" + Playing + } + + export enum AudioStatus { + //% block="playing" + Playing, + //% block="recording" + Recording, + //% block="stopped" + Stopped, + //% block="empty" + BufferEmpty, + } + + export enum BlockingState { + //% block="until done" + Blocking, + //% block="in background" + Nonblocking + } + + let _recordingPresent: boolean = false; + + function audioNotRecording(): boolean { + return !audioIsRecording(); + } + + function audioNotPlaying(): boolean { + return !audioIsPlaying(); + } + + /** + * Record an audio clip for a maximum of 3 seconds + */ + //% block="record audio clip $mode" + //% blockId="record_startRecording" + //% weight=70 + //% parts="microphone" + //% help=record/start-recording + export function startRecording(mode: BlockingState): void { + music._onStopSound(stopPlayback); + eraseRecording(); + record(); + if (mode === BlockingState.Blocking) pauseUntil(audioNotRecording); + _recordingPresent = true; + } + + /** + * Play recorded audio + */ + //% block="play audio clip $mode" + //% blockId="record_playAudio" + //% weight=60 + //% parts="microphone" + //% help=record/play-audio + export function playAudio(mode: BlockingState): void { + play(); + if (mode === BlockingState.Blocking) pauseUntil(audioNotPlaying); + } + + function stopPlayback(): void { + if (audioIsPlaying()) { + stop(); + } + } + + //% shim=record::stop + export function stopRecording(): void { + } + + export function eraseRecording(): void { + _recordingPresent = false; + erase(); + return + } + + /** + * Test what the audio is doing + */ + //% block="audio is $status" + //% blockId="record_audioStatus" + //% parts="microphone" + //% help=record/audio-status + export function audioStatus(status: AudioStatus): boolean { + switch (status) { + case AudioStatus.Playing: + return audioIsPlaying(); + case AudioStatus.Recording: + return audioIsRecording(); + case AudioStatus.Stopped: + return audioIsStopped(); + case AudioStatus.BufferEmpty: + return !_recordingPresent; + } + } + + /** + * Change how sensitive the microphone is. This changes the recording quality! + */ + //% block="set microphone sensitivity to $gain" + //% blockId="record_setMicGain" + //% parts="microphone" + //% weight=30 + //% help=record/set-mic-gain + export function setMicGain(gain: AudioLevels): void { + switch (gain) { + case AudioLevels.Low: + setMicrophoneGain(0.079); + break; + case AudioLevels.Medium: + setMicrophoneGain(0.2); + break; + case AudioLevels.High: + setMicrophoneGain(1.0); + break; + } + } + + /** + * Set the sample frequency for recording, playback, or both (default) + * + * @param hz The sample frequency, in Hz + */ + //% block="set sample rate to $hz || for $scope" + //% hz.label="sample rate" + //% blockId="record_setSampleRate" + //% hz.min=1000 hz.max=22000 hz.defl=11000 + //% expandableArgumentMode="enabled" + //% parts="microphone" + //% weight=40 + //% help=record/set-sample-rate + export function setSampleRate(hz: number, scope?: AudioSampleRateScope): void { + switch (scope) { + case AudioSampleRateScope.Playback: + setOutputSampleRate(hz); + break; + case AudioSampleRateScope.Recording: + setInputSampleRate(hz); + break; + case AudioSampleRateScope.Everything: + default: + setBothSamples(hz); + break; + } + } +} \ No newline at end of file diff --git a/libs/audio-recording/shims.d.ts b/libs/audio-recording/shims.d.ts new file mode 100644 index 00000000000..5a9a8f78f87 --- /dev/null +++ b/libs/audio-recording/shims.d.ts @@ -0,0 +1,77 @@ +// Auto-generated. Do not edit. +declare namespace record { + + /** + * Record an audio clip + */ + //% promise shim=record::record + function record(): void; + + /** + * Play the audio clip that is saved in the buffer + */ + //% shim=record::play + function play(): void; + + /** + * Stop recording + */ + //% shim=record::stop + function stop(): void; + + /** + * Clear the buffer + */ + //% shim=record::erase + function erase(): void; + + /** + * Set sensitity of the microphone input + */ + //% shim=record::setMicrophoneGain + function setMicrophoneGain(gain: number): void; + + /** + * Get how long the recorded audio clip is + */ + //% shim=record::audioDuration + function audioDuration(sampleRate: int32): int32; + + /** + * Get whether the playback is active + */ + //% shim=record::audioIsPlaying + function audioIsPlaying(): boolean; + + /** + * Get whether the microphone is listening + */ + //% shim=record::audioIsRecording + function audioIsRecording(): boolean; + + /** + * Get whether the board is recording or playing back + */ + //% shim=record::audioIsStopped + function audioIsStopped(): boolean; + + /** + * Change the sample rate of the splitter channel (audio input) + */ + //% shim=record::setInputSampleRate + function setInputSampleRate(sampleRate: int32): void; + + /** + * Change the sample rate of the mixer channel (audio output) + */ + //% shim=record::setOutputSampleRate + function setOutputSampleRate(sampleRate: int32): void; + + /** + * Set the sample rate for both input and output + */ + //% shim=record::setBothSamples + function setBothSamples(sampleRate: int32): void; +} + +// Auto-generated. Do not edit. Really. diff --git a/libs/audio-samples/_locales/audio-samples-jsdoc-strings.json b/libs/audio-samples/_locales/audio-samples-jsdoc-strings.json new file mode 100644 index 00000000000..e6e4f0540db --- /dev/null +++ b/libs/audio-samples/_locales/audio-samples-jsdoc-strings.json @@ -0,0 +1,6 @@ +{ + "samples.disable": "Disable audio", + "samples.enable": "Enable audio", + "samples.playAsync": "Play a sample", + "samples.setSampleRate": "Set the sample rate" +} \ No newline at end of file diff --git a/libs/audio-samples/_locales/audio-samples-strings.json b/libs/audio-samples/_locales/audio-samples-strings.json new file mode 100644 index 00000000000..2f2ea637c19 --- /dev/null +++ b/libs/audio-samples/_locales/audio-samples-strings.json @@ -0,0 +1,4 @@ +{ + "samples|block": "samples", + "{id:category}Samples": "Samples" +} \ No newline at end of file diff --git a/libs/audio-samples/pxt.json b/libs/audio-samples/pxt.json new file mode 100644 index 00000000000..95f9da1ba99 --- /dev/null +++ b/libs/audio-samples/pxt.json @@ -0,0 +1,18 @@ +{ + "name": "audio-samples", + "description": "Play audio samples. micro:bit (V2) only", + "dependencies": { + "core": "file:../core" + }, + "weight": 10, + "files": [ + "samples.ts", + "samples.cpp", + "shims.d.ts" + ], + "testFiles": [ + "test.ts" + ], + "public": true, + "searchOnly": true +} \ No newline at end of file diff --git a/libs/audio-samples/samples.cpp b/libs/audio-samples/samples.cpp new file mode 100644 index 00000000000..5232db91b35 --- /dev/null +++ b/libs/audio-samples/samples.cpp @@ -0,0 +1,71 @@ +#include "pxt.h" +#include "MicroBit.h" + +#if MICROBIT_CODAL +#include "SampleSource.h" +#endif + +using namespace pxt; + +namespace samples { + +/** + * Enable audio + */ +//% +void enable() { + #if MICROBIT_CODAL + uBit.audio.enable(); + #else + target_panic(PANIC_VARIANT_NOT_SUPPORTED); + #endif +} + + +/** + * Disable audio + */ +//% +void disable() { + #if MICROBIT_CODAL + uBit.audio.disable(); + #else + target_panic(PANIC_VARIANT_NOT_SUPPORTED); + #endif +} + +/** + * Set the sample rate + */ +//% +void setSampleRate(int src, int sampleRate) { + #if MICROBIT_CODAL + if (0 <= src && src < 4) + uBit.audio.sampleSource[src]->setSampleRate(sampleRate); + #else + target_panic(PANIC_VARIANT_NOT_SUPPORTED); + #endif +} + +bool isValidSample(Buffer buf) { + if (!buf) + return false; + + // TODO: other checks here + return true; +} + +/** + * Play a sample + */ +//% +void playAsync(int src, Buffer buf) { + #if MICROBIT_CODAL + if (0 <= src && src < 4 && isValidSample(buf)) + uBit.audio.sampleSource[src]->playAsync(buf->data, buf->length); + #else + target_panic(PANIC_VARIANT_NOT_SUPPORTED); + #endif +} + +} diff --git a/libs/audio-samples/samples.ts b/libs/audio-samples/samples.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/libs/audio-samples/shims.d.ts b/libs/audio-samples/shims.d.ts new file mode 100644 index 00000000000..f917c6dfec4 --- /dev/null +++ b/libs/audio-samples/shims.d.ts @@ -0,0 +1,29 @@ +// Auto-generated. Do not edit. +declare namespace samples { + + /** + * Enable audio + */ + //% shim=samples::enable + function enable(): void; + + /** + * Disable audio + */ + //% shim=samples::disable + function disable(): void; + + /** + * Set the sample rate + */ + //% shim=samples::setSampleRate + function setSampleRate(src: int32, sampleRate: int32): void; + + /** + * Play a sample + */ + //% shim=samples::playAsync + function playAsync(src: int32, buf: Buffer): void; +} + +// Auto-generated. Do not edit. Really. diff --git a/libs/audio-samples/test.ts b/libs/audio-samples/test.ts new file mode 100644 index 00000000000..3f731bb0069 --- /dev/null +++ b/libs/audio-samples/test.ts @@ -0,0 +1,18 @@ +let strings9 = hex`524946467406000057415645666d74201000000001000100602200006022000001000800646174615006000092a8515977939aae6a4a708998a98e47678297a29d4a5b78939bad7349738a9aa97a426b7d999fa95a587e92a0a4584f73899ba8934b6a8697a78b416277949cae7a4d788d9ca763446f809ba0aa5f5a8391a5944657748b9ca89a516c8b96a8723e6876979caf7f4f7d8ca19d4f4c6f819ca0ad675c8991a87e3f5e728d9da99d55708c99a459436978989caf8554828ca58b42546e849ca1af6f5f8c91a7643f61738f9da8a45b738e9e96464d687b969faf9158868da7713d5b6d859ca2b374638c94a04d4862768f9fa8ab62778da2813c55687f96a0b1965c878fa55c415d70869da1b67e688c9990404f61778ea0a9b0687a8ea16b3c5a667f97a0b39e6287949847495e71869da2ba856c8c9c7b3958607b8ea1aab77079929a54415d6a8196a1b5a7698698873b515d73869ea4be8c6e8e9b623a5c617c8da2abbc7978958d42495c6b8196a3b8ab6e859b7036585d76859ea7c2976f91924b415c637d8da2afc181779b7838525c6e8197a5bbb4738698553b5b5f77869faac4a06f96833c495c667c8da4b1c788769c6038565e6f8198a8bebd74888d40445a627788a0acc8a86e996a35515c697c8fa6b4cd8f7a95473f5760708297aac0c5768c7b354c5a667787a0b0cdb16f955137555d6a7d8fa8b8d3927c88364657636f8397b0c3cd7b8b6531525b677688a2b4d1b4738e3e3f56606b7d8eabb9da967f742d4e5666708498b3c7d380874c34565c697588a5b7d6ba797c2f4655636b7e8eafbede9b7f5a2c5456686f849ab6ccd4857c393c555e6a778aa6badabd7e63294d55666c7f90b2c3e0a278423156596a70859cb9cfd790652c4455626b778aaabfddc27d4a2b5156696b7f92b5c8e0ad68313a555d6a7184a0bdd4d9924c2a4a55646b788cacc5dccb7633345159686c7e95b7cde0b5532941545f6a7286a1c2d5df9334304b58646c778eadc9dcd368243c515d686e7f99bcd1e1b73b294655616a7289a3c7d5e58d1f384c5b656d7892b1cedbd8511e435060686e809cbfd2e5b7262e4757636b738ba7ccd5e97c143f4b5e656e7996b4d2dcdc3e1f4750626970839ec4d2e9ac1436475a626d738fa9cfd5ed690f454b61656f7a98b8d3ded52b244954636a7187a1c8d2ed9a0b3d485d636e7592aed3d6ea5613494c6266707d9bbdd2e3c51f2c4855636b738aa5cbd4ec860e41495e646e7794b1d2dbdf441a484f636772809ec0d4e6b11e314858636c748ca9ccd7e47415414b60656f7a98b7d1e0cb4023475263677384a3c2d5e39b24344a5a646c778eafcadcd56820404e60666f7e9abad1e1b24327475563697586a7c3dada892d334c59666c7b91b3cae0c064293e525f6772819dbad4dd9d492a485464697889acc1dfc77d3c305059686d7e95b6c9e2a7672f3d5360687583a4bbd8d089582a4b55646a7c8db0c0e1af78442f535968708399b8ccda8e71333e5560697888a7badcbb7e632a4d55656b7f91b3c2e0967f4c30555a6972869eb6d0cb7e7d354056606a7a8baabadca27c6e2a4f56666e8395b2c5d580885232565a697689a2b6d3b37488364255616c7f91acbdd78d81752d50576772869ab1c8c374905534555a6a798ea5b8d19c768f3b4257616f8197abc2c8798979314e58667489a0b1ccac6f985a35545c6c7b92a5bbc9877d924142586173849cabc5b571917c354d5968788ea2b3c892739b6037555c707f98a6beba77849547415a627788a0abc69c7095823a4d5b6b7d93a3b6bd7d7c9d6737575d73839ea4c3a1708b964f3f5b637b8ca1aec1817698863c4d5c6e7f99a1bbab6f849c6f37575e7787a0a5c188728f99543e5c667e91a1b1b26f7f968d3e4c5d70849da3bc926b8b9b7538575f7a8ca1a9b87179909d573e5c688197a2b49e65889595424b5e74889ea4b8786f8e9c7b3957637d91a2ada8628190a25e3f5d6b849ba2b585648d9598444c60768e9fa9af67768e9f803a57658096a1b0915c8890a4603f5e6e8b9ca6b070688f979d484a6479949ead9a597e8ea2853d576a8499a2b0795d8d90a7663e62718f9caaa25d6f8f99a04d4a697c989eaf8255848fa5883f586e8a9ba6a762638d91a86c3f6475959bae8b53778e9ca04f4a6c7f9aa1ac68598591a58e415a708e9aab96516c8d95a971406878989cb071527c909da4564c6f859aa4a254618593a590435e74929aae7f4b738b99aa77406a7c9aa0a95c577e919fa55b4d74889ca88f466a8498a496465f7b93997c837d827e837e817e817f` + +samples.enable() +music.setVolume(100) + +while(1) +{ +basic.pause(2000) + +samples.setSampleRate(0,11000) +samples.playAsync(0, strings9) +samples.setSampleRate(1,11000); +samples.playAsync(1, strings9) +samples.setSampleRate(2,6000); +samples.playAsync(2, strings9) +samples.setSampleRate(3,13000); +samples.playAsync(3, strings9) +} diff --git a/libs/bitmap/_locales/arcadeshield-jsdoc-strings.json b/libs/bitmap/_locales/arcadeshield-jsdoc-strings.json new file mode 100644 index 00000000000..1e232b25f01 --- /dev/null +++ b/libs/bitmap/_locales/arcadeshield-jsdoc-strings.json @@ -0,0 +1,40 @@ +{ + "Bitmap.blit": "Copy an image from a source rectangle to a destination rectangle, stretching or\ncompressing to fit the dimensions of the destination rectangle, if necessary.", + "Bitmap.blitRow": "Scale and copy a row of pixels from a texture.", + "Bitmap.clone": "Return a copy of the current bitmap", + "Bitmap.copyFrom": "Sets all pixels in the current bitmap from the other bitmap, which has to be of the same size and\nbpp.", + "Bitmap.doubled": "Stretches the bitmap in both directions by 100%", + "Bitmap.doubledX": "Stretches the bitmap horizontally by 100%", + "Bitmap.doubledY": "Stretches the bitmap vertically by 100%", + "Bitmap.drawBitmap": "Draw given bitmap on the current bitmap", + "Bitmap.drawCircle": "Draw a circle", + "Bitmap.drawIcon": "Draw an icon (monochromatic image) using given color", + "Bitmap.drawLine": "Draw a line", + "Bitmap.drawRect": "Draw an empty rectangle", + "Bitmap.drawTransparentBitmap": "Draw given bitmap with transparent background on the current bitmap", + "Bitmap.fill": "Fill entire bitmap with a given color", + "Bitmap.fillCircle": "Fills a circle", + "Bitmap.fillPolygon4": "Fills a 4-side-polygon", + "Bitmap.fillRect": "Fill a rectangle", + "Bitmap.fillTriangle": "Fills a triangle", + "Bitmap.flipX": "Flips (mirrors) pixels horizontally in the current bitmap", + "Bitmap.flipY": "Flips (mirrors) pixels vertically in the current bitmap", + "Bitmap.getPixel": "Get a pixel color", + "Bitmap.getRows": "Copy row(s) of pixel from bitmap to buffer (8 bit per pixel).", + "Bitmap.height": "Get the height of the bitmap", + "Bitmap.isMono": "True if the bitmap is monochromatic (black and white)", + "Bitmap.overlapsWith": "Check if the current bitmap \"collides\" with another", + "Bitmap.replace": "Replaces one color in an bitmap with another", + "Bitmap.rotated": "Returns an image rotated by -90, 0, 90, 180, 270 deg clockwise", + "Bitmap.scroll": "Every pixel in bitmap is moved by (dx,dy)", + "Bitmap.setPixel": "Set pixel color", + "Bitmap.setRows": "Copy row(s) of pixel from buffer to bitmap.", + "Bitmap.transposed": "Returns a transposed bitmap (with X/Y swapped)", + "Bitmap.width": "Get the width of the bitmap", + "ScreenBitmap.brightness": "Gets current screen backlight brightness (0-100)", + "ScreenBitmap.setBrightness": "Sets the screen backlight brightness (10-100)", + "bitmap.create": "Create new empty (transparent) bitmap", + "bitmap.doubledIcon": "Double the size of an icon", + "bitmap.ofBuffer": "Create new bitmap with given content", + "helpers.imageRotated": "Returns an image rotated by 90, 180, 270 deg clockwise" +} \ No newline at end of file diff --git a/libs/bitmap/_locales/arcadeshield-strings.json b/libs/bitmap/_locales/arcadeshield-strings.json new file mode 100644 index 00000000000..9b7d6eab3c4 --- /dev/null +++ b/libs/bitmap/_locales/arcadeshield-strings.json @@ -0,0 +1,8 @@ +{ + "bitmap|block": "bitmap", + "helpers|block": "helpers", + "{id:category}Bitmap": "Bitmap", + "{id:category}Helpers": "Helpers", + "{id:category}ScreenBitmap": "ScreenBitmap", + "{id:category}_helpers_workaround": "_helpers_workaround" +} \ No newline at end of file diff --git a/libs/bitmap/_locales/bitmap-jsdoc-strings.json b/libs/bitmap/_locales/bitmap-jsdoc-strings.json new file mode 100644 index 00000000000..da6d74a269a --- /dev/null +++ b/libs/bitmap/_locales/bitmap-jsdoc-strings.json @@ -0,0 +1,38 @@ +{ + "Bitmap.blit": "Copy an image from a source rectangle to a destination rectangle, stretching or\ncompressing to fit the dimensions of the destination rectangle, if necessary.", + "Bitmap.blitRow": "Scale and copy a row of pixels from a texture.", + "Bitmap.clone": "Return a copy of the current bitmap", + "Bitmap.copyFrom": "Sets all pixels in the current bitmap from the other bitmap, which has to be of the same size and\nbpp.", + "Bitmap.doubled": "Stretches the bitmap in both directions by 100%", + "Bitmap.doubledX": "Stretches the bitmap horizontally by 100%", + "Bitmap.doubledY": "Stretches the bitmap vertically by 100%", + "Bitmap.drawBitmap": "Draw given bitmap on the current bitmap", + "Bitmap.drawCircle": "Draw a circle", + "Bitmap.drawIcon": "Draw an icon (monochromatic image) using given color", + "Bitmap.drawLine": "Draw a line", + "Bitmap.drawRect": "Draw an empty rectangle", + "Bitmap.drawTransparentBitmap": "Draw given bitmap with transparent background on the current bitmap", + "Bitmap.fill": "Fill entire bitmap with a given color", + "Bitmap.fillCircle": "Fills a circle", + "Bitmap.fillPolygon4": "Fills a 4-side-polygon", + "Bitmap.fillRect": "Fill a rectangle", + "Bitmap.fillTriangle": "Fills a triangle", + "Bitmap.flipX": "Flips (mirrors) pixels horizontally in the current bitmap", + "Bitmap.flipY": "Flips (mirrors) pixels vertically in the current bitmap", + "Bitmap.getPixel": "Get a pixel color", + "Bitmap.getRows": "Copy row(s) of pixel from bitmap to buffer (8 bit per pixel).", + "Bitmap.height": "Get the height of the bitmap", + "Bitmap.isMono": "True if the bitmap is monochromatic (black and white)", + "Bitmap.overlapsWith": "Check if the current bitmap \"collides\" with another", + "Bitmap.replace": "Replaces one color in an bitmap with another", + "Bitmap.rotated": "Returns an image rotated by -90, 0, 90, 180, 270 deg clockwise", + "Bitmap.scroll": "Every pixel in bitmap is moved by (dx,dy)", + "Bitmap.setPixel": "Set pixel color", + "Bitmap.setRows": "Copy row(s) of pixel from buffer to bitmap.", + "Bitmap.transposed": "Returns a transposed bitmap (with X/Y swapped)", + "Bitmap.width": "Get the width of the bitmap", + "bitmaps.create": "Create new empty (transparent) bitmap", + "bitmaps.doubledIcon": "Double the size of an icon", + "bitmaps.ofBuffer": "Create new bitmap with given content", + "helpers.imageRotated": "Returns an image rotated by 90, 180, 270 deg clockwise" +} \ No newline at end of file diff --git a/libs/bitmap/_locales/bitmap-strings.json b/libs/bitmap/_locales/bitmap-strings.json new file mode 100644 index 00000000000..edd1d850698 --- /dev/null +++ b/libs/bitmap/_locales/bitmap-strings.json @@ -0,0 +1,7 @@ +{ + "bitmaps|block": "bitmaps", + "helpers|block": "helpers", + "{id:category}Bitmap": "Bitmap", + "{id:category}Bitmaps": "Bitmaps", + "{id:category}Helpers": "Helpers" +} \ No newline at end of file diff --git a/libs/bitmap/bitmap.cpp b/libs/bitmap/bitmap.cpp new file mode 100644 index 00000000000..997f32fa848 --- /dev/null +++ b/libs/bitmap/bitmap.cpp @@ -0,0 +1,1565 @@ +#include "pxt.h" + +typedef RefImage *Bitmap_; + +#define IMAGE_BITS 4 + +#if IMAGE_BITS == 1 +// OK +#elif IMAGE_BITS == 4 +// OK +#else +#error "Invalid IMAGE_BITS" +#endif + +#define XX(v) (int)(((int16_t)(v))) +#define YY(v) (int)(((int16_t)(((int32_t)(v)) >> 16))) + +namespace pxt { + +PXT_VTABLE(RefImage, ValType::Object) + +void RefImage::destroy(RefImage *t) {} + +void RefImage::print(RefImage *t) { + DMESG("RefImage %p size=%d x %d", t, t->width(), t->height()); +} + +int RefImage::wordHeight() { + if (bpp() == 1) + oops(20); + return ((height() * 4 + 31) >> 5); +} + +void RefImage::makeWritable() { + ++revision; + if (buffer->isReadOnly()) { + buffer = mkBuffer(data(), length()); + } +} + +uint8_t RefImage::fillMask(color c) { + return this->bpp() == 1 ? (c & 1) * 0xff : 0x11 * (c & 0xf); +} + +bool RefImage::inRange(int x, int y) { + return 0 <= x && x < width() && 0 <= y && y < height(); +} + +void RefImage::clamp(int *x, int *y) { + *x = min(max(*x, 0), width() - 1); + *y = min(max(*y, 0), height() - 1); +} + +RefImage::RefImage(BoxedBuffer *buf) : PXT_VTABLE_INIT(RefImage), buffer(buf) { + revision = 0; + if (!buf) + oops(21); +} + +static inline int byteSize(int w, int h, int bpp) { + if (bpp == 1) + return sizeof(ImageHeader) + ((h + 7) >> 3) * w; + else + return sizeof(ImageHeader) + (((h * 4 + 31) / 32) * 4) * w; +} + +Bitmap_ allocImage(const uint8_t *data, uint32_t sz) { + auto buf = mkBuffer(data, sz); + registerGCObj(buf); + Bitmap_ r = NEW_GC(RefImage, buf); + unregisterGCObj(buf); + return r; +} + +Bitmap_ mkImage(int width, int height, int bpp) { + if (width < 0 || height < 0 || width > 2000 || height > 2000) + return NULL; + if (bpp != 1 && bpp != 4) + return NULL; + uint32_t sz = byteSize(width, height, bpp); + Bitmap_ r = allocImage(NULL, sz); + auto hd = r->header(); + hd->magic = IMAGE_HEADER_MAGIC; + hd->bpp = bpp; + hd->width = width; + hd->height = height; + hd->padding = 0; + MEMDBG("mkImage: %d X %d => %p", width, height, r); + return r; +} + +bool isValidImage(Buffer buf) { + if (!buf || buf->length < 9) + return false; + + auto hd = (ImageHeader *)(buf->data); + if (hd->magic != IMAGE_HEADER_MAGIC || (hd->bpp != 1 && hd->bpp != 4)) + return false; + + int sz = byteSize(hd->width, hd->height, hd->bpp); + if (sz != (int)buf->length) + return false; + + return true; +} + +bool isLegacyImage(Buffer buf) { + if (!buf || buf->length < 5) + return false; + + if (buf->data[0] != 0xe1 && buf->data[0] != 0xe4) + return false; + + int sz = byteSize(buf->data[1], buf->data[2], buf->data[0] & 0xf) - 4; + if (sz != (int)buf->length) + return false; + + return true; +} + +} // namespace pxt + +namespace BitmapMethods { + +/** + * Get underlying buffer + */ +//% property +Buffer __buffer(Bitmap_ img) { + // only for simulator + return NULL; +} + +/** + * Get the width of the bitmap + */ +//% property +int width(Bitmap_ img) { + return img->width(); +} + +/** + * Get the height of the bitmap + */ +//% property +int height(Bitmap_ img) { + return img->height(); +} + +/** + * True if the bitmap is monochromatic (black and white) + */ +//% property +bool isMono(Bitmap_ img) { + return img->bpp() == 1; +} + +//% property +bool isStatic(Bitmap_ img) { + return img->buffer->isReadOnly(); +} + +//% property +bool revision(Bitmap_ img) { + return img->revision; +} + +/** + * Sets all pixels in the current bitmap from the other bitmap, which has to be of the same size and + * bpp. + */ +//% +void copyFrom(Bitmap_ img, Bitmap_ from) { + if (img->width() != from->width() || img->height() != from->height() || + img->bpp() != from->bpp()) + return; + img->makeWritable(); + memcpy(img->pix(), from->pix(), from->pixLength()); +} + +static void setCore(Bitmap_ img, int x, int y, int c) { + auto ptr = img->pix(x, y); + if (img->bpp() == 4) { + if (y & 1) + *ptr = (*ptr & 0x0f) | (c << 4); + else + *ptr = (*ptr & 0xf0) | (c & 0xf); + } else if (img->bpp() == 1) { + uint8_t mask = 0x01 << (y & 7); + if (c) + *ptr |= mask; + else + *ptr &= ~mask; + } +} + +static int getCore(Bitmap_ img, int x, int y) { + auto ptr = img->pix(x, y); + if (img->bpp() == 4) { + if (y & 1) + return *ptr >> 4; + else + return *ptr & 0x0f; + } else if (img->bpp() == 1) { + uint8_t mask = 0x01 << (y & 7); + return (*ptr & mask) ? 1 : 0; + } + return 0; +} + +/** + * Set pixel color + */ +//% +void setPixel(Bitmap_ img, int x, int y, int c) { + if (!img->inRange(x, y)) + return; + img->makeWritable(); + setCore(img, x, y, c); +} + +/** + * Get a pixel color + */ +//% +int getPixel(Bitmap_ img, int x, int y) { + if (!img->inRange(x, y)) + return 0; + return getCore(img, x, y); +} + +void fillRect(Bitmap_ img, int x, int y, int w, int h, int c); + +/** + * Fill entire bitmap with a given color + */ +//% +void fill(Bitmap_ img, int c) { + if (c && img->hasPadding()) { + fillRect(img, 0, 0, img->width(), img->height(), c); + return; + } + img->makeWritable(); + memset(img->pix(), img->fillMask(c), img->pixLength()); +} + +/** + * Copy row(s) of pixel from bitmap to buffer (8 bit per pixel). + */ +//% +void getRows(Bitmap_ img, int x, Buffer dst) { + if (img->bpp() != 4) + return; + + int w = img->width(); + int h = img->height(); + if (x >= w || x < 0) + return; + + uint8_t *sp = img->pix(x, 0); + uint8_t *dp = dst->data; + int n = min(dst->length, (w - x) * h) >> 1; + + while (n--) { + *dp++ = *sp & 0xf; + *dp++ = *sp >> 4; + sp++; + } +} + +/** + * Copy row(s) of pixel from buffer to bitmap. + */ +//% +void setRows(Bitmap_ img, int x, Buffer src) { + if (img->bpp() != 4) + return; + + int w = img->width(); + int h = img->height(); + if (x >= w || x < 0) + return; + + img->makeWritable(); + + uint8_t *dp = img->pix(x, 0); + uint8_t *sp = src->data; + int n = min(src->length, (w - x) * h) >> 1; + + while (n--) { + *dp++ = (sp[0] & 0xf) | (sp[1] << 4); + sp += 2; + } +} + +void fillRect(Bitmap_ img, int x, int y, int w, int h, int c) { + if (w == 0 || h == 0 || x >= img->width() || y >= img->height()) + return; + + int x2 = x + w - 1; + int y2 = y + h - 1; + + if (x2 < 0 || y2 < 0) + return; + + img->clamp(&x2, &y2); + img->clamp(&x, &y); + w = x2 - x + 1; + h = y2 - y + 1; + + if (!img->hasPadding() && x == 0 && y == 0 && w == img->width() && h == img->height()) { + fill(img, c); + return; + } + + img->makeWritable(); + + auto bh = img->byteHeight(); + uint8_t f = img->fillMask(c); + + uint8_t *p = img->pix(x, y); + while (w-- > 0) { + if (img->bpp() == 1) { + auto ptr = p; + unsigned mask = 0x01 << (y & 7); + + for (int i = 0; i < h; ++i) { + if (mask == 0x100) { + if (h - i >= 8) { + *++ptr = f; + i += 7; + continue; + } else { + mask = 0x01; + ++ptr; + } + } + if (c) + *ptr |= mask; + else + *ptr &= ~mask; + mask <<= 1; + } + + } else if (img->bpp() == 4) { + auto ptr = p; + unsigned mask = 0x0f; + if (y & 1) + mask <<= 4; + + for (int i = 0; i < h; ++i) { + if (mask == 0xf00) { + if (h - i >= 2) { + *++ptr = f; + i++; + continue; + } else { + mask = 0x0f; + ptr++; + } + } + *ptr = (*ptr & ~mask) | (f & mask); + mask <<= 4; + } + } + p += bh; + } +} + +//% +void _fillRect(Bitmap_ img, int xy, int wh, int c) { + fillRect(img, XX(xy), YY(xy), XX(wh), YY(wh), c); +} + +void mapRect(Bitmap_ img, int x, int y, int w, int h, Buffer map) { + if (w == 0 || h == 0 || x >= img->width() || y >= img->height()) + return; + + if (img->bpp() != 4 || map->length < 16) + return; + + int x2 = x + w - 1; + int y2 = y + h - 1; + + if (x2 < 0 || y2 < 0) + return; + + img->clamp(&x2, &y2); + img->clamp(&x, &y); + w = x2 - x + 1; + h = y2 - y + 1; + + img->makeWritable(); + + auto bh = img->byteHeight(); + auto m = map->data; + uint8_t *p = img->pix(x, y); + while (w-- > 0) { + auto ptr = p; + unsigned shift = y & 1; + for (int i = 0; i < h; i++) { + if (shift) { + *ptr = (m[*ptr >> 4] << 4) | (*ptr & 0x0f); + ptr++; + shift = 0; + } else { + *ptr = (m[*ptr & 0xf] & 0xf) | (*ptr & 0xf0); + shift = 1; + } + } + p += bh; + } +} + +//% +void _mapRect(Bitmap_ img, int xy, int wh, Buffer c) { + mapRect(img, XX(xy), YY(xy), XX(wh), YY(wh), c); +} + +//% argsNullable +bool equals(Bitmap_ img, Bitmap_ other) { + if (!other) { + return false; + } + auto len = img->length(); + if (len != other->length()) { + return false; + } + return 0 == memcmp(img->data(), other->data(), len); +} + +/** + * Return a copy of the current bitmap + */ +//% +Bitmap_ clone(Bitmap_ img) { + auto r = allocImage(img->data(), img->length()); + MEMDBG("mkImageClone: %d X %d => %p", img->width(), img->height(), r); + return r; +} + +/** + * Flips (mirrors) pixels horizontally in the current bitmap + */ +//% +void flipX(Bitmap_ img) { + img->makeWritable(); + + int bh = img->byteHeight(); + auto a = img->pix(); + auto b = img->pix(img->width() - 1, 0); + + uint8_t tmp[bh]; + + while (a < b) { + memcpy(tmp, a, bh); + memcpy(a, b, bh); + memcpy(b, tmp, bh); + a += bh; + b -= bh; + } +} + +/** + * Flips (mirrors) pixels vertically in the current bitmap + */ +//% +void flipY(Bitmap_ img) { + img->makeWritable(); + + // this is quite slow - for small 16x16 sprite it will take in the order of 1ms + // something faster requires quite a bit of bit tweaking, especially for mono bitmaps + for (int i = 0; i < img->width(); ++i) { + int a = 0; + int b = img->height() - 1; + while (a < b) { + int tmp = getCore(img, i, a); + setCore(img, i, a, getCore(img, i, b)); + setCore(img, i, b, tmp); + a++; + b--; + } + } +} + +/** + * Returns a transposed bitmap (with X/Y swapped) + */ +//% +Bitmap_ transposed(Bitmap_ img) { + Bitmap_ r = mkImage(img->height(), img->width(), img->bpp()); + + // this is quite slow + for (int i = 0; i < img->width(); ++i) { + for (int j = 0; j < img->height(); ++i) { + setCore(r, j, i, getCore(img, i, j)); + } + } + + return r; +} + +void drawBitmap(Bitmap_ img, Bitmap_ from, int x, int y); + +/** + * Every pixel in bitmap is moved by (dx,dy) + */ +//% +void scroll(Bitmap_ img, int dx, int dy) { + img->makeWritable(); + auto bh = img->byteHeight(); + auto w = img->width(); + if (dy != 0) { + // TODO one day we may want a more memory-efficient implementation + auto img2 = clone(img); + fill(img, 0); + drawBitmap(img, img2, dx, dy); + } else if (dx < 0) { + dx = -dx; + if (dx < w) + memmove(img->pix(), img->pix(dx, 0), (w - dx) * bh); + else + dx = w; + memset(img->pix(w - dx, 0), 0, dx * bh); + } else if (dx > 0) { + if (dx < w) + memmove(img->pix(dx, 0), img->pix(), (w - dx) * bh); + else + dx = w; + memset(img->pix(), 0, dx * bh); + } +} + +const uint8_t bitdouble[] = {0x00, 0x03, 0x0c, 0x0f, 0x30, 0x33, 0x3c, 0x3f, + 0xc0, 0xc3, 0xcc, 0xcf, 0xf0, 0xf3, 0xfc, 0xff}; +const uint8_t nibdouble[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff}; + +/** + * Stretches the bitmap horizontally by 100% + */ +//% +Bitmap_ doubledX(Bitmap_ img) { + if (img->width() > 126) + return NULL; + + Bitmap_ r = mkImage(img->width() * 2, img->height(), img->bpp()); + auto src = img->pix(); + auto dst = r->pix(); + auto w = img->width(); + auto bh = img->byteHeight(); + + for (int i = 0; i < w; ++i) { + memcpy(dst, src, bh); + dst += bh; + memcpy(dst, src, bh); + dst += bh; + + src += bh; + } + + return r; +} + +/** + * Stretches the bitmap vertically by 100% + */ +//% +Bitmap_ doubledY(Bitmap_ img) { + if (img->height() > 126) + return NULL; + + Bitmap_ r = mkImage(img->width(), img->height() * 2, img->bpp()); + auto src0 = img->pix(); + auto dst = r->pix(); + + auto w = img->width(); + auto sbh = img->byteHeight(); + auto bh = r->byteHeight(); + auto dbl = img->bpp() == 1 ? bitdouble : nibdouble; + + for (int i = 0; i < w; ++i) { + auto src = src0 + i * sbh; + for (int j = 0; j < bh; j += 2) { + *dst++ = dbl[*src & 0xf]; + if (j != bh - 1) + *dst++ = dbl[*src >> 4]; + src++; + } + } + + return r; +} + +/** + * Replaces one color in an bitmap with another + */ +//% +void replace(Bitmap_ img, int from, int to) { + if (img->bpp() != 4) + return; + to &= 0xf; + if (from == to) + return; + + img->makeWritable(); + + // avoid bleeding 'to' color into the overflow areas of the picture + if (from == 0 && img->hasPadding()) { + for (int i = 0; i < img->height(); ++i) + for (int j = 0; j < img->width(); ++j) + if (getCore(img, j, i) == from) + setCore(img, j, i, to); + return; + } + + auto ptr = img->pix(); + auto len = img->pixLength(); + while (len--) { + auto b = *ptr; + if ((b & 0xf) == from) + b = (b & 0xf0) | to; + if ((b >> 4) == from) + b = (to << 4) | (b & 0xf); + *ptr++ = b; + } +} + +/** + * Stretches the bitmap in both directions by 100% + */ +//% +Bitmap_ doubled(Bitmap_ img) { + Bitmap_ tmp = doubledX(img); + registerGCObj(tmp); + Bitmap_ r = doubledY(tmp); + unregisterGCObj(tmp); + return r; +} + +bool drawBitmapCore(Bitmap_ img, Bitmap_ from, int x, int y, int color) { + auto w = from->width(); + auto h = from->height(); + auto sh = img->height(); + auto sw = img->width(); + + if (x + w <= 0) + return false; + if (x >= sw) + return false; + if (y + h <= 0) + return false; + if (y >= sh) + return false; + + auto len = y < 0 ? min(sh, h + y) : min(sh - y, h); + auto tbp = img->bpp(); + auto fbp = from->bpp(); + auto y0 = y; + + if (color == -2 && x == 0 && y == 0 && tbp == fbp && w == sw && h == sh) { + copyFrom(img, from); + return false; + } + + // DMESG("drawIMG(%d,%d) at (%d,%d) w=%d bh=%d len=%d", + // w,h,x, y, img->width(), img->byteHeight(), len ); + + auto fromH = from->byteHeight(); + auto imgH = img->byteHeight(); + auto fromBase = from->pix(); + auto imgBase = img->pix(0, y); + +#define LOOPHD \ + for (int xx = 0; xx < w; ++xx, ++x) \ + if (0 <= x && x < sw) + + if (tbp == 4 && fbp == 4) { + auto wordH = fromH >> 2; + LOOPHD { + y = y0; + + auto fdata = (uint32_t *)fromBase + wordH * xx; + auto tdata = imgBase + imgH * x; + + // DMESG("%d,%d xx=%d/%d - %p (%p) -- %d",x,y,xx,w,tdata,img->pix(), + // (uint8_t*)fdata - from->pix()); + + auto cnt = wordH; + auto bot = min(sh, y + h); + +#define COLS(s) ((v >> (s)) & 0xf) +#define COL(s) COLS(s) + +#define STEPA(s) \ + if (COL(s) && 0 <= y && y < bot) \ + SETLOW(s); \ + y++; +#define STEPB(s) \ + if (COL(s) && 0 <= y && y < bot) \ + SETHIGH(s); \ + y++; \ + tdata++; +#define STEPAQ(s) \ + if (COL(s)) \ + SETLOW(s); +#define STEPBQ(s) \ + if (COL(s)) \ + SETHIGH(s); \ + tdata++; + +// perf: expanded version 5% faster +#define ORDER(A, B) \ + A(0); \ + B(4); \ + A(8); \ + B(12); \ + A(16); \ + B(20); \ + A(24); \ + B(28) +//#define ORDER(A,B) for (int k = 0; k < 32; k += 8) { A(k); B(4+k); } +#define LOOP(A, B, xbot) \ + while (cnt--) { \ + auto v = *fdata++; \ + if (0 <= y && y <= xbot - 8) { \ + ORDER(A##Q, B##Q); \ + y += 8; \ + } else { \ + ORDER(A, B); \ + } \ + } +#define LOOPS(xbot) \ + if (y & 1) \ + LOOP(STEPB, STEPA, xbot) \ + else \ + LOOP(STEPA, STEPB, xbot) + + if (color >= 0) { +#define SETHIGH(s) *tdata = (*tdata & 0x0f) | ((COLS(s)) << 4) +#define SETLOW(s) *tdata = (*tdata & 0xf0) | COLS(s) + LOOPS(sh) + } else if (color == -2) { +#undef COL +#define COL(s) 1 + LOOPS(bot) + } else { +#undef COL +#define COL(s) COLS(s) +#undef SETHIGH +#define SETHIGH(s) \ + if (*tdata & 0xf0) \ + return true +#undef SETLOW +#define SETLOW(s) \ + if (*tdata & 0x0f) \ + return true + LOOPS(sh) + } + } + } else if (tbp == 1 && fbp == 1) { + auto left = img->pix() - imgBase; + auto right = img->pix(0, img->height() - 1) - imgBase; + LOOPHD { + y = y0; + + auto data = fromBase + fromH * xx; + auto off = imgBase + imgH * x; + auto off0 = off + left; + auto off1 = off + right; + + int shift = (y & 7); + + int y1 = y + h + (y & 7); + int prev = 0; + + while (y < y1 - 8) { + int curr = *data++ << shift; + if (off0 <= off && off <= off1) { + uint8_t v = (curr >> 0) | (prev >> 8); + + if (color == -1) { + if (*off & v) + return true; + } else { + *off |= v; + } + } + off++; + prev = curr; + y += 8; + } + + int left = y1 - y; + if (left > 0) { + int curr = *data << shift; + if (off0 <= off && off <= off1) { + uint8_t v = ((curr >> 0) | (prev >> 8)) & (0xff >> (8 - left)); + if (color == -1) { + if (*off & v) + return true; + } else { + *off |= v; + } + } + } + } + } else if (tbp == 4 && fbp == 1) { + if (y < 0) { + fromBase = from->pix(0, -y); + imgBase = img->pix(); + } + // icon mode + LOOPHD { + auto fdata = fromBase + fromH * xx; + auto tdata = imgBase + imgH * x; + + unsigned mask = 0x01; + auto v = *fdata++; + int off = (y & 1) ? 1 : 0; + if (y < 0) { + mask <<= -y & 7; + off = 0; + } + for (int i = off; i < len + off; ++i) { + if (mask == 0x100) { + mask = 0x01; + v = *fdata++; + } + if (v & mask) { + if (i & 1) + *tdata = (*tdata & 0x0f) | (color << 4); + else + *tdata = (*tdata & 0xf0) | color; + } + mask <<= 1; + if (i & 1) + tdata++; + } + } + } + + return false; +} + +/** + * Draw given bitmap on the current bitmap + */ +//% +void drawBitmap(Bitmap_ img, Bitmap_ from, int x, int y) { + img->makeWritable(); + if (img->bpp() == 4 && from->bpp() == 4) { + drawBitmapCore(img, from, x, y, -2); + } else { + fillRect(img, x, y, from->width(), from->height(), 0); + drawBitmapCore(img, from, x, y, 0); + } +} + +/** + * Draw given bitmap with transparent background on the current bitmap + */ +//% +void drawTransparentBitmap(Bitmap_ img, Bitmap_ from, int x, int y) { + img->makeWritable(); + drawBitmapCore(img, from, x, y, 0); +} + +/** + * Check if the current bitmap "collides" with another + */ +//% +bool overlapsWith(Bitmap_ img, Bitmap_ other, int x, int y) { + return drawBitmapCore(img, other, x, y, -1); +} + +// Bitmap_ format (legacy) +// byte 0: magic 0xe4 - 4 bit color; 0xe1 is monochromatic +// byte 1: width in pixels +// byte 2: height in pixels +// byte 3: padding (should be zero) +// byte 4...N: data 4 bits per pixels, high order nibble printed first, lines aligned to 32 bit +// words byte 4...N: data 1 bit per pixels, high order bit printed first, lines aligned to byte + +Bitmap_ convertAndWrap(Buffer buf) { + if (isValidImage(buf)) + return NEW_GC(RefImage, buf); + + // What follows in this function is mostly dead code, except if people construct bitmap buffers + // by hand. Probably safe to remove in a year (middle of 2020) or so. When removing, also remove + // from sim. + if (!isLegacyImage(buf)) + return NULL; + + auto tmp = mkBuffer(NULL, buf->length + 4); + auto hd = (ImageHeader *)tmp->data; + auto src = buf->data; + hd->magic = IMAGE_HEADER_MAGIC; + hd->bpp = src[0] & 0xf; + hd->width = src[1]; + hd->height = src[2]; + hd->padding = 0; + memcpy(hd->pixels, src + 4, buf->length - 4); + + registerGCObj(tmp); + auto r = NEW_GC(RefImage, tmp); + unregisterGCObj(tmp); + return r; +} + +//% +void _drawIcon(Bitmap_ img, Buffer icon, int xy, int c) { + img->makeWritable(); + + auto iconImg = convertAndWrap(icon); + if (!iconImg || iconImg->bpp() != 1) + return; + + drawBitmapCore(img, iconImg, XX(xy), YY(xy), c); +} + +static void drawLineLow(Bitmap_ img, int x0, int y0, int x1, int y1, int c) { + int dx = x1 - x0; + int dy = y1 - y0; + int yi = 1; + if (dy < 0) { + yi = -1; + dy = -dy; + } + int D = 2 * dy - dx; + dx <<= 1; + dy <<= 1; + int y = y0; + for (int x = x0; x <= x1; ++x) { + setCore(img, x, y, c); + if (D > 0) { + y += yi; + D -= dx; + } + D += dy; + } +} + +static void drawLineHigh(Bitmap_ img, int x0, int y0, int x1, int y1, int c) { + int dx = x1 - x0; + int dy = y1 - y0; + int xi = 1; + if (dx < 0) { + xi = -1; + dx = -dx; + } + int D = 2 * dx - dy; + dx <<= 1; + dy <<= 1; + int x = x0; + for (int y = y0; y <= y1; ++y) { + setCore(img, x, y, c); + if (D > 0) { + x += xi; + D -= dy; + } + D += dx; + } +} + +void drawLine(Bitmap_ img, int x0, int y0, int x1, int y1, int c) { + if (x1 < x0) { + drawLine(img, x1, y1, x0, y0, c); + return; + } + int w = x1 - x0; + int h = y1 - y0; + + if (h == 0) { + if (w == 0) + setPixel(img, x0, y0, c); + else + fillRect(img, x0, y0, w + 1, 1, c); + return; + } + + if (w == 0) { + if (h > 0) + fillRect(img, x0, y0, 1, h + 1, c); + else + fillRect(img, x0, y1, 1, -h + 1, c); + return; + } + + if (x1 < 0 || x0 >= img->width()) + return; + if (x0 < 0) { + y0 -= (h * x0 / w); + x0 = 0; + } + if (x1 >= img->width()) { + int d = (img->width() - 1) - x1; + y1 += (h * d / w); + x1 = img->width() - 1; + } + + if (y0 < y1) { + if (y0 >= img->height() || y1 < 0) + return; + if (y0 < 0) { + x0 -= (w * y0 / h); + y0 = 0; + } + if (y1 >= img->height()) { + int d = (img->height() - 1) - y1; + x1 += (w * d / h); + y1 = img->height() - 1; + } + } else { + if (y1 >= img->height() || y0 < 0) + return; + if (y1 < 0) { + x1 -= (w * y1 / h); + y1 = 0; + } + if (y0 >= img->height()) { + int d = (img->height() - 1) - y0; + x0 += (w * d / h); + y0 = img->height() - 1; + } + } + + img->makeWritable(); + + if (h < 0) { + h = -h; + if (h < w) + drawLineLow(img, x0, y0, x1, y1, c); + else + drawLineHigh(img, x1, y1, x0, y0, c); + } else { + if (h < w) + drawLineLow(img, x0, y0, x1, y1, c); + else + drawLineHigh(img, x0, y0, x1, y1, c); + } +} + +//% +void _drawLine(Bitmap_ img, int xy, int wh, int c) { + drawLine(img, XX(xy), YY(xy), XX(wh), YY(wh), c); +} + +void blitRow(Bitmap_ img, int x, int y, Bitmap_ from, int fromX, int fromH) { + if (!img->inRange(x, 0) || !img->inRange(fromX, 0) || fromH <= 0) + return; + + if (img->bpp() != 4 || from->bpp() != 4) + return; + + int fy = 0; + int stepFY = (from->width() << 16) / fromH; + int endY = y + fromH; + if (endY > img->height()) + endY = img->height(); + if (y < 0) { + fy += -y * stepFY; + y = 0; + } + + auto dp = img->pix(x, y); + auto sp = from->pix(fromX, 0); + + while (y < endY) { + int p = fy >> 16, c; + if (p & 1) + c = sp[p >> 1] >> 4; + else + c = sp[p >> 1] & 0xf; + if (y & 1) { + *dp = (*dp & 0x0f) | (c << 4); + dp++; + } else { + *dp = (*dp & 0xf0) | (c & 0xf); + } + y++; + fy += stepFY; + } +} + +//% +void _blitRow(Bitmap_ img, int xy, Bitmap_ from, int xh) { + blitRow(img, XX(xy), YY(xy), from, XX(xh), YY(xh)); +} + +bool blit(Bitmap_ dst, Bitmap_ src, pxt::RefCollection *args) { + int xDst = pxt::toInt(args->getAt(0)); + int yDst = pxt::toInt(args->getAt(1)); + int wDst = pxt::toInt(args->getAt(2)); + int hDst = pxt::toInt(args->getAt(3)); + int xSrc = pxt::toInt(args->getAt(4)); + int ySrc = pxt::toInt(args->getAt(5)); + int wSrc = pxt::toInt(args->getAt(6)); + int hSrc = pxt::toInt(args->getAt(7)); + bool transparent = pxt::toBoolQuick(args->getAt(8)); + bool check = pxt::toBoolQuick(args->getAt(9)); + + int xSrcStep = (wSrc << 16) / wDst; + int ySrcStep = (hSrc << 16) / hDst; + + int xDstClip = abs(min(0, xDst)); + int yDstClip = abs(min(0, yDst)); + int xDstStart = xDst + xDstClip; + int yDstStart = yDst + yDstClip; + int xDstEnd = min(dst->width(), xDst + wDst); + int yDstEnd = min(dst->height(), yDst + hDst); + + int xSrcStart = max(0, (xSrc << 16) + xDstClip * xSrcStep); + int ySrcStart = max(0, (ySrc << 16) + yDstClip * ySrcStep); + int xSrcEnd = min(src->width(), xSrc + wSrc) << 16; + int ySrcEnd = min(src->height(), ySrc + hSrc) << 16; + + if (!check) + dst->makeWritable(); + + for (int yDstCur = yDstStart, ySrcCur = ySrcStart; yDstCur < yDstEnd && ySrcCur < ySrcEnd; ++yDstCur, ySrcCur += ySrcStep) { + int ySrcCurI = ySrcCur >> 16; + for (int xDstCur = xDstStart, xSrcCur = xSrcStart; xDstCur < xDstEnd && xSrcCur < xSrcEnd; ++xDstCur, xSrcCur += xSrcStep) { + int xSrcCurI = xSrcCur >> 16; + int cSrc = getCore(src, xSrcCurI, ySrcCurI); + if (check && cSrc) { + int cDst = getCore(dst, xDstCur, yDstCur); + if (cDst) { + return true; + } + continue; + } + if (!transparent || cSrc) { + setCore(dst, xDstCur, yDstCur, cSrc); + } + } + } + return false; +} + +//% +bool _blit(Bitmap_ img, Bitmap_ src, pxt::RefCollection *args) { + return blit(img, src, args); +} + +void fillCircle(Bitmap_ img, int cx, int cy, int r, int c) { + int x = r - 1; + int y = 0; + int dx = 1; + int dy = 1; + int err = dx - (r << 1); + + while (x >= y) { + fillRect(img, cx + x, cy - y, 1, 1 + (y << 1), c); + fillRect(img, cx + y, cy - x, 1, 1 + (x << 1), c); + fillRect(img, cx - x, cy - y, 1, 1 + (y << 1), c); + fillRect(img, cx - y, cy - x, 1, 1 + (x << 1), c); + if (err <= 0) { + ++y; + err += dy; + dy += 2; + } else { + --x; + dx += 2; + err += dx - (r << 1); + } + } +} + +//% +void _fillCircle(Bitmap_ img, int cxy, int r, int c) { + fillCircle(img, XX(cxy), YY(cxy), r, c); +} + +typedef struct +{ + int x, y; + int x0, y0; + int x1, y1; + int W,H; + int dx, dy; + int yi, xi; + int D; + int nextFuncIndex; +} LineGenState; // For keeping track of the state when generating Y values for a line, even when moving to the next X. + +typedef struct +{ + int min; + int max; +} ValueRange; + +void nextYRange_Low(int x, LineGenState *line, ValueRange *yRange) { + while (line->x == x && line->x <= line->x1 && line->x < line->W) { + if (0 <= line->x) { + if (line->y < yRange->min) yRange->min = line->y; + if (line->y > yRange->max) yRange->max = line->y; + } + if (line->D > 0) { + line->y += line->yi; + line->D -= line->dx; + } + line->D += line->dy; + ++line->x; + } +} + +void nextYRange_HighUp(int x, LineGenState *line, ValueRange *yRange) { + while (line->x == x && line->y >= line->y1 && line->x < line->W) { + if (0 <= line->x) { + if (line->y < yRange->min) yRange->min = line->y; + if (line->y > yRange->max) yRange->max = line->y; + } + if (line->D > 0) { + line->x += line->xi; + line->D += line->dy; + } + line->D += line->dx; + --line->y; + } +} +// This function is similar to the sub-function drawLineHigh for drawLine. However, it yields back after calculating all Y values of a given X. When the function is called again, it continues from the state where it yielded back previously. +void nextYRange_HighDown(int x, LineGenState *line, ValueRange *yRange) { + while (line->x == x && line->y <= line->y1 && line->x < line->W) { + if (0 <= line->x) { + if (line->y < yRange->min) yRange->min = line->y; + if (line->y > yRange->max) yRange->max = line->y; + } + if (line->D > 0) { + line->x += line->xi; + line->D -= line->dy; + } + line->D += line->dx; + ++line->y; + } +} + +LineGenState initYRangeGenerator(int16_t X0, int16_t Y0, int16_t X1, int16_t Y1) { + LineGenState line; + + line.x0 = X0, line.y0 = Y0, line.x1 = X1, line.y1 = Y1; + + line.dx = line.x1 - line.x0; + line.dy = line.y1 - line.y0; + line.y = line.y0; + line.x = line.x0; + + if ((line.dy < 0 ? -line.dy : line.dy) < line.dx) { + line.yi = 1; + if (line.dy < 0) { + line.yi = -1; + line.dy = -line.dy; + } + line.D = 2 * line.dy - line.dx; + line.dx <<= 1; + line.dy <<= 1; + + line.nextFuncIndex = 0; + return line; + } else { + line.xi = 1; + // if (dx < 0) {//should not hit + // PANIC(); + // } + if (line.dy < 0) { + line.D = 2 * line.dx + line.dy; + line.dx <<= 1; + line.dy <<= 1; + + line.nextFuncIndex = 1; + return line; + } else { + line.D = 2 * line.dx - line.dy; + line.dx <<= 1; + line.dy <<= 1; + + line.nextFuncIndex = 2; + return line; + } + } +} + +// core of draw vertical line for repeatly calling, eg.: fillTriangle() or fillPolygon4() +// value range/safety check not included +// prepare "img->makeWritable();" and "uint8_t f = img->fillMask(c);" outside required. +// bpp=4 support only right now +void drawVLineCore(Bitmap_ img, int x, int y, int h, uint8_t f) { + uint8_t *p = img->pix(x, y); + auto ptr = p; + unsigned mask = 0x0f; + if (y & 1) + mask <<= 4; + for (int i = 0; i < h; ++i) { + if (mask == 0xf00) { + if (h - i >= 2) { + *++ptr = f; + i++; + continue; + } else { + mask = 0x0f; + ptr++; + } + } + *ptr = (*ptr & ~mask) | (f & mask); + mask <<= 4; + } +} + +void drawVLine(Bitmap_ img, int x, int y, int h, int c) { + int H = height(img); + uint8_t f = img->fillMask(c); + if (x < 0 || x >= width(img) || y >= H || y + h - 1 < 0) + return; + if (y < 0){ + h += y; + y = 0; + } + if (y + h > H) + h = H - y; + drawVLineCore(img, x, y, h, f); +} + +void fillTriangle(Bitmap_ img, int x0, int y0, int x1, int y1, int x2, int y2, int c) { + if (x1 < x0) { + pxt::swap(x0, x1); + pxt::swap(y0, y1); + } + if (x2 < x1) { + pxt::swap(x1, x2); + pxt::swap(y1, y2); + } + if (x1 < x0) { + pxt::swap(x0, x1); + pxt::swap(y0, y1); + } + + LineGenState lines[] = { + initYRangeGenerator(x0, y0, x2, y2), + initYRangeGenerator(x0, y0, x1, y1), + initYRangeGenerator(x1, y1, x2, y2) + }; + + int W = width(img), H = height(img); + lines[0].W = lines[1].W = lines[2].W = W; + lines[0].H = lines[1].H = lines[2].H = H; + + // We have 3 different sub-functions to generate Ys of edges, each particular edge maps to one of them. + // Use function pointers to avoid judging which function to call at every X. + typedef void (*FP_NEXT)(int x, LineGenState *line, ValueRange *yRange); + FP_NEXT nextFuncList[] = { nextYRange_Low, nextYRange_HighUp, nextYRange_HighDown }; + FP_NEXT fpNext0 = nextFuncList[lines[0].nextFuncIndex]; + FP_NEXT fpNext1 = nextFuncList[lines[1].nextFuncIndex]; + FP_NEXT fpNext2 = nextFuncList[lines[2].nextFuncIndex]; + + ValueRange yRange = {H, -1}; + img->makeWritable(); + uint8_t f = img->fillMask(c); + + for (int x = lines[1].x0; x <= min(x1, W - 1); x++) { + yRange.min = H; + yRange.max = -1; + fpNext0(x, &lines[0], &yRange); + fpNext1(x, &lines[1], &yRange); + + if (x < 0 || yRange.min >= H || yRange.max < 0) + continue; + if (yRange.min < 0) + yRange.min = 0; + if (yRange.max >= H) + yRange.max = H - 1; + drawVLineCore(img, x, yRange.min, yRange.max - yRange.min + 1, f); + } + + fpNext2(lines[2].x0, &lines[2], &yRange); + + for (int x = lines[2].x0 + 1; x <= min(x2, W - 1); x++) { + yRange.min = H; + yRange.max = -1; + fpNext0(x, &lines[0], &yRange); + fpNext2(x, &lines[2], &yRange); + + if (x < 0 || yRange.min >= H || yRange.max < 0) + continue; + if (yRange.min < 0) + yRange.min = 0; + if (yRange.max >= H) + yRange.max = H - 1; + drawVLineCore(img, x, yRange.min, yRange.max - yRange.min + 1, f); + } +} + +void fillPolygon4(Bitmap_ img, int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3, int c) { + LineGenState lines[] = { + (x0 < x1) ? initYRangeGenerator(x0, y0, x1, y1) : initYRangeGenerator(x1, y1, x0, y0), + (x1 < x2) ? initYRangeGenerator(x1, y1, x2, y2) : initYRangeGenerator(x2, y2, x1, y1), + (x2 < x3) ? initYRangeGenerator(x2, y2, x3, y3) : initYRangeGenerator(x3, y3, x2, y2), + (x0 < x3) ? initYRangeGenerator(x0, y0, x3, y3) : initYRangeGenerator(x3, y3, x0, y0)}; + + int W = width(img), H = height(img); + lines[0].W = lines[1].W = lines[2].W = lines[3].W = W; + lines[0].H = lines[1].H = lines[2].H = lines[3].H = H; + + int minX = min(min(x0, x1), min(x2, x3)); + int maxX = min(max(max(x0, x1), max(x2, x3)), W - 1); + + typedef void (*FP_NEXT)(int x, LineGenState *line, ValueRange *yRange); + FP_NEXT nextFuncList[] = { nextYRange_Low, nextYRange_HighUp, nextYRange_HighDown }; + FP_NEXT fpNext0 = nextFuncList[lines[0].nextFuncIndex]; + FP_NEXT fpNext1 = nextFuncList[lines[1].nextFuncIndex]; + FP_NEXT fpNext2 = nextFuncList[lines[2].nextFuncIndex]; + FP_NEXT fpNext3 = nextFuncList[lines[3].nextFuncIndex]; + + ValueRange yRange = { H, -1 }; + img->makeWritable(); + uint8_t f = img->fillMask(c); + + for (int x = minX; x <= maxX; x++) { + yRange.min = H; + yRange.max = -1; + fpNext0(x, &lines[0], &yRange); + fpNext1(x, &lines[1], &yRange); + fpNext2(x, &lines[2], &yRange); + fpNext3(x, &lines[3], &yRange); + + if (x < 0 || yRange.min >= H || yRange.max < 0) + continue; + if (yRange.min < 0) + yRange.min = 0; + if (yRange.max >= H) + yRange.max = H - 1; + drawVLineCore(img, x, yRange.min, yRange.max - yRange.min + 1, f); + } +} + +//% +void _fillTriangle(Bitmap_ img, pxt::RefCollection *args) { + fillTriangle( + img, + pxt::toInt(args->getAt(0)), + pxt::toInt(args->getAt(1)), + pxt::toInt(args->getAt(2)), + pxt::toInt(args->getAt(3)), + pxt::toInt(args->getAt(4)), + pxt::toInt(args->getAt(5)), + pxt::toInt(args->getAt(6)) + ); +} + +// This polygon fill is similar to fillTriangle(): Scan minY and maxY of all edges at each X, and draw a vertical line between (x,minY)~(x,maxY). +// The main difference is that it sorts the endpoints of each edge, x0 < x1, to draw from left to right, but doesn't sort the edges as it's too time consuming. +// Instead, just call next(), which returns immediately if the x is not in range of the edge in horizon. +// NOTE: Unlike triangles, edges of a polygon can cross a vertical line at a given X multi time. This algorithm can fill correctly only if edges meet this condition: Any vertical line(x) cross edges at most 2 times. +// Fortunately, no matter what perspective transform is applied, a rectangle/trapezoid will still meet this condition. +// Ref: https://forum.makecode.com/t/new-3d-engine-help-filling-4-sided-polygons/18641/9 +//% +void _fillPolygon4(Bitmap_ img, pxt::RefCollection *args) { + fillPolygon4( + img, + pxt::toInt(args->getAt(0)), + pxt::toInt(args->getAt(1)), + pxt::toInt(args->getAt(2)), + pxt::toInt(args->getAt(3)), + pxt::toInt(args->getAt(4)), + pxt::toInt(args->getAt(5)), + pxt::toInt(args->getAt(6)), + pxt::toInt(args->getAt(7)), + pxt::toInt(args->getAt(8)) + ); +} + +} // namespace BitmapMethods + +namespace bitmaps { +/** + * Create new bitmap with given content + */ +//% +Bitmap_ ofBuffer(Buffer buf) { + return BitmapMethods::convertAndWrap(buf); +} +} + +namespace bitmaps { +/** + * Create new empty (transparent) bitmap + */ +//% +Bitmap_ create(int width, int height) { + Bitmap_ r = mkImage(width, height, IMAGE_BITS); + if (r) + memset(r->pix(), 0, r->pixLength()); + else + target_panic(PANIC_INVALID_IMAGE); + return r; +} + +/** + * Double the size of an icon + */ +//% +Buffer doubledIcon(Buffer icon) { + if (!isValidImage(icon)) + return NULL; + + auto r = NEW_GC(RefImage, icon); + registerGCObj(r); + auto t = BitmapMethods::doubled(r); + unregisterGCObj(r); + return t->buffer; +} + +} // namespace bitmaps + +// This is 6.5x faster than standard on word-aligned copy +// probably should move to codal + +#ifndef __linux__ +extern "C" void *memcpy(void *dst, const void *src, size_t sz) { + void *dst0 = dst; + if (sz >= 4 && !((uintptr_t)dst & 3) && !((uintptr_t)src & 3)) { + size_t cnt = sz >> 2; + uint32_t *d = (uint32_t *)dst; + const uint32_t *s = (const uint32_t *)src; + while (cnt--) { + *d++ = *s++; + } + sz &= 3; + dst = d; + src = s; + } + + // see comment in memset() below (have not seen optimization here, but better safe than sorry) + volatile uint8_t *dd = (uint8_t *)dst; + volatile uint8_t *ss = (uint8_t *)src; + + while (sz--) { + *dd++ = *ss++; + } + + return dst0; +} + +extern "C" void *memset(void *dst, int v, size_t sz) { + void *dst0 = dst; + if (sz >= 4 && !((uintptr_t)dst & 3)) { + size_t cnt = sz >> 2; + uint32_t vv = 0x01010101 * v; + uint32_t *d = (uint32_t *)dst; + while (cnt--) { + *d++ = vv; + } + sz &= 3; + dst = d; + } + + // without volatile here, GCC may optimize the loop to memset() call which is obviously not great + volatile uint8_t *dd = (uint8_t *)dst; + + while (sz--) { + *dd++ = v; + } + + return dst0; +} +#endif diff --git a/libs/bitmap/bitmap.ts b/libs/bitmap/bitmap.ts new file mode 100644 index 00000000000..c92fc4b4a64 --- /dev/null +++ b/libs/bitmap/bitmap.ts @@ -0,0 +1,259 @@ +type color = number + +namespace bitmaps { + export function repeatY(count: number, image: Bitmap) { + let arr = [image] + while (--count > 0) + arr.push(image) + return concatY(arr) + } + + export function concatY(images: Bitmap[]) { + let w = 0 + let h = 0 + for (let img of images) { + w = Math.max(img.width, w) + h += img.height + } + let r = bitmaps.create(w, h) + let y = 0 + for (let img of images) { + let x = (w - img.width) >> 1 + r.drawBitmap(img, x, y) + y += img.height + } + return r + } +} + + +//% snippet='bmp` `' +//% pySnippet='bmp(""" """)' +interface Bitmap { + /** + * Draw an icon (monochromatic image) using given color + */ + //% helper=imageDrawIcon + drawIcon(icon: Buffer, x: number, y: number, c: color): void; + + /** + * Fill a rectangle + */ + //% helper=imageFillRect + fillRect(x: number, y: number, w: number, h: number, c: color): void; + + /** + * Draw a line + */ + //% helper=imageDrawLine + drawLine(x0: number, y0: number, x1: number, y1: number, c: color): void; + + /** + * Draw an empty rectangle + */ + //% helper=imageDrawRect + drawRect(x: number, y: number, w: number, h: number, c: color): void; + + /** + * Draw a circle + */ + //% helper=imageDrawCircle + drawCircle(cx: number, cy: number, r: number, c: color): void; + + /** + * Fills a circle + */ + //% helper=imageFillCircle + fillCircle(cx: number, cy: number, r: number, c: color): void; + + /** + * Fills a triangle + */ + //% helper=imageFillTriangle + fillTriangle(x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, col: number): void; + + /** + * Fills a 4-side-polygon + */ + //% helper=imageFillPolygon4 + fillPolygon4(x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, col: number): void; + + /** + * Returns an image rotated by -90, 0, 90, 180, 270 deg clockwise + */ + //% helper=imageRotated + rotated(deg: number): Bitmap; + + /** + * Scale and copy a row of pixels from a texture. + */ + //% helper=imageBlitRow + blitRow(dstX: number, dstY: number, from: Bitmap, fromX: number, fromH: number): void; + + /** + * Copy an image from a source rectangle to a destination rectangle, stretching or + * compressing to fit the dimensions of the destination rectangle, if necessary. + */ + //% helper=imageBlit + blit(xDst: number, yDst: number, wDst: number, hDst: number, src: Bitmap, xSrc: number, ySrc: number, wSrc: number, hSrc: number, transparent: boolean, check: boolean): boolean; +} + +namespace helpers { + //% shim=BitmapMethods::_drawLine + function _drawLine(img: Bitmap, xy: number, wh: number, c: color): void { } + + //% shim=BitmapMethods::_fillRect + function _fillRect(img: Bitmap, xy: number, wh: number, c: color): void { } + + //% shim=BitmapMethods::_mapRect + function _mapRect(img: Bitmap, xy: number, wh: number, m: Buffer): void { } + + //% shim=BitmapMethods::_drawIcon + function _drawIcon(img: Bitmap, icon: Buffer, xy: number, c: color): void { } + + //% shim=BitmapMethods::_fillCircle + declare function _fillCircle(img: Bitmap, cxy: number, r: number, c: color): void; + + //% shim=BitmapMethods::_blitRow + declare function _blitRow(img: Bitmap, xy: number, from: Bitmap, xh: number): void; + + //% shim=BitmapMethods::_blit + declare function _blit(img: Bitmap, src: Bitmap, args: number[]): boolean; + + //% shim=BitmapMethods::_fillTriangle + declare function _fillTriangle(img: Bitmap, args: number[]): void; + + //% shim=BitmapMethods::_fillPolygon4 + declare function _fillPolygon4(img: Bitmap, args: number[]): void; + + function pack(x: number, y: number) { + return (Math.clamp(-30000, 30000, x | 0) & 0xffff) | (Math.clamp(-30000, 30000, y | 0) << 16) + } + + let _blitArgs: number[]; + + export function imageBlit(img: Bitmap, xDst: number, yDst: number, wDst: number, hDst: number, src: Bitmap, xSrc: number, ySrc: number, wSrc: number, hSrc: number, transparent: boolean, check: boolean): boolean { + _blitArgs = _blitArgs || []; + _blitArgs[0] = xDst | 0; + _blitArgs[1] = yDst | 0; + _blitArgs[2] = wDst | 0; + _blitArgs[3] = hDst | 0; + _blitArgs[4] = xSrc | 0; + _blitArgs[5] = ySrc | 0; + _blitArgs[6] = wSrc | 0; + _blitArgs[7] = hSrc | 0; + _blitArgs[8] = transparent ? 1 : 0; + _blitArgs[9] = check ? 1 : 0; + return _blit(img, src, _blitArgs); + } + + export function imageBlitRow(img: Bitmap, dstX: number, dstY: number, from: Bitmap, fromX: number, fromH: number): void { + _blitRow(img, pack(dstX, dstY), from, pack(fromX, fromH)) + } + + export function imageDrawIcon(img: Bitmap, icon: Buffer, x: number, y: number, c: color): void { + _drawIcon(img, icon, pack(x, y), c) + } + export function imageFillRect(img: Bitmap, x: number, y: number, w: number, h: number, c: color): void { + _fillRect(img, pack(x, y), pack(w, h), c) + } + export function imageMapRect(img: Bitmap, x: number, y: number, w: number, h: number, m: Buffer): void { + _mapRect(img, pack(x, y), pack(w, h), m) + } + export function imageDrawLine(img: Bitmap, x: number, y: number, w: number, h: number, c: color): void { + _drawLine(img, pack(x, y), pack(w, h), c) + } + export function imageDrawRect(img: Bitmap, x: number, y: number, w: number, h: number, c: color): void { + if (w == 0 || h == 0) return + w-- + h-- + imageDrawLine(img, x, y, x + w, y, c) + imageDrawLine(img, x, y, x, y + h, c) + imageDrawLine(img, x + w, y + h, x + w, y, c) + imageDrawLine(img, x + w, y + h, x, y + h, c) + } + + export function imageDrawCircle(img: Bitmap, cx: number, cy: number, r: number, col: number) { + cx = cx | 0; + cy = cy | 0; + r = r | 0; + // short cuts + if (r < 0) + return; + + // Bresenham's algorithm + let x = 0 + let y = r + let d = 3 - 2 * r + + while (y >= x) { + img.setPixel(cx + x, cy + y, col) + img.setPixel(cx - x, cy + y, col) + img.setPixel(cx + x, cy - y, col) + img.setPixel(cx - x, cy - y, col) + img.setPixel(cx + y, cy + x, col) + img.setPixel(cx - y, cy + x, col) + img.setPixel(cx + y, cy - x, col) + img.setPixel(cx - y, cy - x, col) + x++ + if (d > 0) { + y-- + d += 4 * (x - y) + 10 + } else { + d += 4 * x + 6 + } + } + } + + export function imageFillCircle(img: Bitmap, cx: number, cy: number, r: number, col: number) { + _fillCircle(img, pack(cx, cy), r, col); + } + + export function imageFillTriangle(img: Bitmap, x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, col: number) { + _blitArgs = _blitArgs || []; + _blitArgs[0] = x0; + _blitArgs[1] = y0; + _blitArgs[2] = x1; + _blitArgs[3] = y1; + _blitArgs[4] = x2; + _blitArgs[5] = y2; + _blitArgs[6] = col; + _fillTriangle(img, _blitArgs); + } + + export function imageFillPolygon4(img: Bitmap, x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, col: number) { + _blitArgs = _blitArgs || []; + _blitArgs[0] = x0; + _blitArgs[1] = y0; + _blitArgs[2] = x1; + _blitArgs[3] = y1; + _blitArgs[4] = x2; + _blitArgs[5] = y2; + _blitArgs[6] = x3; + _blitArgs[7] = y3; + _blitArgs[8] = col; + _fillPolygon4(img, _blitArgs); + } + + /** + * Returns an image rotated by 90, 180, 270 deg clockwise + */ + export function imageRotated(img: Bitmap, deg: number) { + if (deg == -90 || deg == 270) { + let r = img.transposed(); + r.flipY(); + return r; + } else if (deg == 180 || deg == -180) { + let r = img.clone(); + r.flipX(); + r.flipY(); + return r; + } else if (deg == 90) { + let r = img.transposed(); + r.flipX(); + return r; + } else { + return null; + } + } +} diff --git a/libs/bitmap/pxt.json b/libs/bitmap/pxt.json new file mode 100644 index 00000000000..49bc50dfb66 --- /dev/null +++ b/libs/bitmap/pxt.json @@ -0,0 +1,19 @@ +{ + "name": "bitmap", + "description": "Support for bitmaps (micro:bit V2 only).", + "dependencies": { + "core": "file:../core" + }, + "files": [ + "bitmap.cpp", + "bitmap.ts", + "shims.d.ts" + ], + "public": true, + "hidden": true, + "searchOnly": true, + "preferredEditor": "tsprj", + "disablesVariants": [ + "mbdal" + ] +} diff --git a/libs/bitmap/shims.d.ts b/libs/bitmap/shims.d.ts new file mode 100644 index 00000000000..3c20787423c --- /dev/null +++ b/libs/bitmap/shims.d.ts @@ -0,0 +1,159 @@ +// Auto-generated. Do not edit. + + +declare interface Bitmap { + /** + * Get underlying buffer + */ + //% property shim=BitmapMethods::__buffer + __buffer: Buffer; + + /** + * Get the width of the bitmap + */ + //% property shim=BitmapMethods::width + width: int32; + + /** + * Get the height of the bitmap + */ + //% property shim=BitmapMethods::height + height: int32; + + /** + * True if the bitmap is monochromatic (black and white) + */ + //% property shim=BitmapMethods::isMono + isMono: boolean; + + /** + * Sets all pixels in the current bitmap from the other bitmap, which has to be of the same size and + * bpp. + */ + //% shim=BitmapMethods::copyFrom + copyFrom(from: Bitmap): void; + + /** + * Set pixel color + */ + //% shim=BitmapMethods::setPixel + setPixel(x: int32, y: int32, c: int32): void; + + /** + * Get a pixel color + */ + //% shim=BitmapMethods::getPixel + getPixel(x: int32, y: int32): int32; + + /** + * Fill entire bitmap with a given color + */ + //% shim=BitmapMethods::fill + fill(c: int32): void; + + /** + * Copy row(s) of pixel from bitmap to buffer (8 bit per pixel). + */ + //% shim=BitmapMethods::getRows + getRows(x: int32, dst: Buffer): void; + + /** + * Copy row(s) of pixel from buffer to bitmap. + */ + //% shim=BitmapMethods::setRows + setRows(x: int32, src: Buffer): void; + + /** + * Return a copy of the current bitmap + */ + //% shim=BitmapMethods::clone + clone(): Bitmap; + + /** + * Flips (mirrors) pixels horizontally in the current bitmap + */ + //% shim=BitmapMethods::flipX + flipX(): void; + + /** + * Flips (mirrors) pixels vertically in the current bitmap + */ + //% shim=BitmapMethods::flipY + flipY(): void; + + /** + * Returns a transposed bitmap (with X/Y swapped) + */ + //% shim=BitmapMethods::transposed + transposed(): Bitmap; + + /** + * Every pixel in bitmap is moved by (dx,dy) + */ + //% shim=BitmapMethods::scroll + scroll(dx: int32, dy: int32): void; + + /** + * Stretches the bitmap horizontally by 100% + */ + //% shim=BitmapMethods::doubledX + doubledX(): Bitmap; + + /** + * Stretches the bitmap vertically by 100% + */ + //% shim=BitmapMethods::doubledY + doubledY(): Bitmap; + + /** + * Replaces one color in an bitmap with another + */ + //% shim=BitmapMethods::replace + replace(from: int32, to: int32): void; + + /** + * Stretches the bitmap in both directions by 100% + */ + //% shim=BitmapMethods::doubled + doubled(): Bitmap; + + /** + * Draw given bitmap on the current bitmap + */ + //% shim=BitmapMethods::drawBitmap + drawBitmap(from: Bitmap, x: int32, y: int32): void; + + /** + * Draw given bitmap with transparent background on the current bitmap + */ + //% shim=BitmapMethods::drawTransparentBitmap + drawTransparentBitmap(from: Bitmap, x: int32, y: int32): void; + + /** + * Check if the current bitmap "collides" with another + */ + //% shim=BitmapMethods::overlapsWith + overlapsWith(other: Bitmap, x: int32, y: int32): boolean; +} +declare namespace bitmaps { + + /** + * Create new bitmap with given content + */ + //% shim=bitmaps::ofBuffer + function ofBuffer(buf: Buffer): Bitmap; + + /** + * Create new empty (transparent) bitmap + */ + //% shim=bitmaps::create + function create(width: int32, height: int32): Bitmap; + + /** + * Double the size of an icon + */ + //% shim=bitmaps::doubledIcon + function doubledIcon(icon: Buffer): Buffer; +} + +// Auto-generated. Do not edit. Really. diff --git a/libs/bluetooth/_locales/bluetooth-strings.json b/libs/bluetooth/_locales/bluetooth-strings.json index e63707af103..0bdbd8fcb63 100644 --- a/libs/bluetooth/_locales/bluetooth-strings.json +++ b/libs/bluetooth/_locales/bluetooth-strings.json @@ -1,6 +1,7 @@ { "bluetooth.advertiseUid|block": "bluetooth advertise UID|namespace (bytes 6-9)%ns|instance (bytes 2-6)%instance|with power %power|connectable %connectable", "bluetooth.advertiseUrl|block": "bluetooth advertise url %url|with power %power|connectable %connectable", + "bluetooth.advertiseUrl|param|url|defl": "https://makecode.com", "bluetooth.onBluetoothConnected|block": "on bluetooth connected", "bluetooth.onBluetoothDisconnected|block": "on bluetooth disconnected", "bluetooth.onUartDataReceived|block": "bluetooth|on data received %delimiters=serial_delimiter_conv", @@ -18,6 +19,7 @@ "bluetooth.uartWriteNumber|block": "bluetooth uart|write number %value", "bluetooth.uartWriteString|block": "bluetooth uart|write string %data", "bluetooth.uartWriteValue|block": "bluetooth uart|write value %name|= %value", + "bluetooth.uartWriteValue|param|name|defl": "x", "bluetooth|block": "bluetooth", "{id:category}Bluetooth": "Bluetooth" } \ No newline at end of file diff --git a/libs/bluetooth/bluetooth.cpp b/libs/bluetooth/bluetooth.cpp index e9ba548bed6..2ba454e530a 100644 --- a/libs/bluetooth/bluetooth.cpp +++ b/libs/bluetooth/bluetooth.cpp @@ -145,6 +145,7 @@ namespace bluetooth { */ //% help=bluetooth/on-uart-data-received //% weight=18 blockId=bluetooth_on_data_received block="bluetooth|on data received %delimiters=serial_delimiter_conv" + //% delimiters.label="delimiter" void onUartDataReceived(String delimiters, Action body) { startUartService(); uart->eventOn(MSTR(delimiters)); @@ -181,8 +182,10 @@ namespace bluetooth { * @param connectable true to keep bluetooth connectable for other services, false otherwise. */ //% blockId=eddystone_advertise_url block="bluetooth advertise url %url|with power %power|connectable %connectable" + //% url.label="url" power.label="power" connectable.label="connectable" //% parts=bluetooth weight=11 blockGap=8 //% help=bluetooth/advertise-url blockExternalInputs=1 + //% hidden=1 deprecated=1 void advertiseUrl(String url, int power, bool connectable) { #if CONFIG_ENABLED(MICROBIT_BLE_EDDYSTONE_URL) power = min(MICROBIT_BLE_POWER_LEVELS-1, max(0, power)); @@ -198,7 +201,7 @@ namespace bluetooth { * @param power power level between 0 and 7, eg: 7 * @param connectable true to keep bluetooth connectable for other services, false otherwise. */ - //% parts=bluetooth weight=12 advanced=true + //% parts=bluetooth weight=12 advanced=true deprecated=1 void advertiseUidBuffer(Buffer nsAndInstance, int power, bool connectable) { #if CONFIG_ENABLED(MICROBIT_BLE_EDDYSTONE_UID) auto buf = nsAndInstance; @@ -216,6 +219,7 @@ namespace bluetooth { */ //% parts=bluetooth weight=5 help=bluetooth/set-transmit-power advanced=true //% blockId=bluetooth_settransmitpower block="bluetooth set transmit power %power" + //% power.label="value" void setTransmitPower(int power) { uBit.bleManager.setTransmitPower(min(MICROBIT_BLE_POWER_LEVELS-1, max(0, power))); } @@ -226,6 +230,7 @@ namespace bluetooth { //% blockId=eddystone_stop_advertising block="bluetooth stop advertising" //% parts=bluetooth weight=10 //% help=bluetooth/stop-advertising advanced=true + //% hidden=1 deprecated=1 void stopAdvertising() { uBit.bleManager.stopAdvertising(); } diff --git a/libs/bluetooth/bluetooth.ts b/libs/bluetooth/bluetooth.ts index fdcd4f13da7..f922613b494 100644 --- a/libs/bluetooth/bluetooth.ts +++ b/libs/bluetooth/bluetooth.ts @@ -20,6 +20,7 @@ namespace bluetooth { */ //% help=bluetooth/uart-write-string weight=80 //% blockId=bluetooth_uart_write block="bluetooth uart|write string %data" blockGap=8 + //% data.label="value" //% parts="bluetooth" shim=bluetooth::uartWriteString advanced=true export function uartWriteString(data: string): void { console.log(data) @@ -30,6 +31,7 @@ namespace bluetooth { */ //% help=bluetooth/uart-write-line weight=79 //% blockId=bluetooth_uart_line block="bluetooth uart|write line %data" blockGap=8 + //% data.label="value" //% parts="bluetooth" advanced=true export function uartWriteLine(data: string): void { uartWriteString(data + serial.NEW_LINE); @@ -41,6 +43,7 @@ namespace bluetooth { //% help=bluetooth/uart-write-number weight=79 //% weight=89 blockGap=8 advanced=true //% blockId=bluetooth_uart_writenumber block="bluetooth uart|write number %value" + //% value.label="value" export function uartWriteNumber(value: number): void { uartWriteString(value.toString()); } @@ -53,6 +56,7 @@ namespace bluetooth { //% weight=88 weight=78 //% help=bluetooth/uart-write-value advanced=true //% blockId=bluetooth_uart_writevalue block="bluetooth uart|write value %name|= %value" + //% name.label="name" value.label="value" export function uartWriteValue(name: string, value: number): void { uartWriteString((name ? name + ":" : "") + value + NEW_LINE); } @@ -62,6 +66,7 @@ namespace bluetooth { */ //% help=bluetooth/uart-read-until weight=75 //% blockId=bluetooth_uart_read block="bluetooth uart|read until %del=serial_delimiter_conv" + //% del.label="delimiter" //% parts="bluetooth" shim=bluetooth::uartReadUntil advanced=true export function uartReadUntil(del: string): string { // dummy implementation for simulator @@ -76,8 +81,10 @@ namespace bluetooth { * @param connectable true to keep bluetooth connectable for other services, false otherwise. */ //% blockId=eddystone_advertise_uid block="bluetooth advertise UID|namespace (bytes 6-9)%ns|instance (bytes 2-6)%instance|with power %power|connectable %connectable" + //% ns.label="namespace" instance.label="instance" power.label="power" connectable.label="connectable" //% parts=bluetooth weight=12 blockGap=8 //% help=bluetooth/advertise-uid blockExternalInputs=1 + //% hidden=1 deprecated=1 export function advertiseUid(ns: number, instance: number, power: number, connectable: boolean) { const buf = pins.createBuffer(16); buf.setNumber(NumberFormat.Int32BE, 6, ns); diff --git a/libs/bluetooth/pxt.json b/libs/bluetooth/pxt.json index f11afc30ec5..19d1ccce015 100644 --- a/libs/bluetooth/pxt.json +++ b/libs/bluetooth/pxt.json @@ -10,6 +10,8 @@ "BLEHF2Service.h", "BLEHF2Service.cpp" ], + "weight": 10, + "searchOnly": true, "icon": "./static/packages/bluetooth/icon.png", "public": true, "dependencies": { diff --git a/libs/bluetooth/shims.d.ts b/libs/bluetooth/shims.d.ts index 46f84bf0cb9..5493e3ef278 100644 --- a/libs/bluetooth/shims.d.ts +++ b/libs/bluetooth/shims.d.ts @@ -109,7 +109,8 @@ declare namespace bluetooth { */ //% blockId=eddystone_advertise_url block="bluetooth advertise url %url|with power %power|connectable %connectable" //% parts=bluetooth weight=11 blockGap=8 - //% help=bluetooth/advertise-url blockExternalInputs=1 shim=bluetooth::advertiseUrl + //% help=bluetooth/advertise-url blockExternalInputs=1 + //% hidden=1 deprecated=1 shim=bluetooth::advertiseUrl function advertiseUrl(url: string, power: int32, connectable: boolean): void; /** @@ -118,7 +119,7 @@ declare namespace bluetooth { * @param power power level between 0 and 7, eg: 7 * @param connectable true to keep bluetooth connectable for other services, false otherwise. */ - //% parts=bluetooth weight=12 advanced=true shim=bluetooth::advertiseUidBuffer + //% parts=bluetooth weight=12 advanced=true deprecated=1 shim=bluetooth::advertiseUidBuffer function advertiseUidBuffer(nsAndInstance: Buffer, power: int32, connectable: boolean): void; /** @@ -134,8 +135,9 @@ declare namespace bluetooth { */ //% blockId=eddystone_stop_advertising block="bluetooth stop advertising" //% parts=bluetooth weight=10 - //% help=bluetooth/stop-advertising advanced=true shim=bluetooth::stopAdvertising + //% help=bluetooth/stop-advertising advanced=true + //% hidden=1 deprecated=1 shim=bluetooth::stopAdvertising function stopAdvertising(): void; } -// Auto-generated. Do not edit. Really. +// Auto-generated. Do not edit. Really. \ No newline at end of file diff --git a/libs/color/_locales/color-jsdoc-strings.json b/libs/color/_locales/color-jsdoc-strings.json new file mode 100644 index 00000000000..5d1c5f9f349 --- /dev/null +++ b/libs/color/_locales/color-jsdoc-strings.json @@ -0,0 +1,23 @@ +{ + "ColorHues": "Well known color hues", + "Colors": "Well known colors", + "color": "Color manipulation", + "color.ColorBuffer": "A buffer of colors", + "color.ColorBuffer.write": "Writes the content of the src color buffer starting at the start dstOffset in the current buffer", + "color.ColorBuffer.write|param|dstOffset": "@param src", + "color.ColorBufferLayout.ARGB": "32bit RGB color with alpha", + "color.ColorBufferLayout.RGB": "24bit RGB color", + "color.createBuffer": "Converts an array of colors into a color buffer", + "color.fade": "Fade the color by the brightness", + "color.fade|param|brightness": "the amount of brightness to apply to the color, eg: 128", + "color.fade|param|color": "color to fade", + "color.hsv": "Convert an HSV (hue, saturation, value) color to RGB", + "color.hsv|param|hue": "value of the hue channel between 0 and 255. eg: 255", + "color.hsv|param|sat": "value of the saturation channel between 0 and 255. eg: 255", + "color.hsv|param|val": "value of the value channel between 0 and 255. eg: 255", + "color.rgb": "Converts red, green, blue channels into a RGB color", + "color.rgb|param|blue": "value of the blue channel between 0 and 255. eg: 255", + "color.rgb|param|green": "value of the green channel between 0 and 255. eg: 255", + "color.rgb|param|red": "value of the red channel between 0 and 255. eg: 255", + "color.wellKnown": "Get the RGB value of a known color" +} \ No newline at end of file diff --git a/libs/color/_locales/color-strings.json b/libs/color/_locales/color-strings.json new file mode 100644 index 00000000000..59d4fe53d43 --- /dev/null +++ b/libs/color/_locales/color-strings.json @@ -0,0 +1,31 @@ +{ + "ColorHues.Aqua|block": "aqua", + "ColorHues.Blue|block": "blue", + "ColorHues.Green|block": "green", + "ColorHues.Magenta|block": "magenta", + "ColorHues.Orange|block": "orange", + "ColorHues.Pink|block": "pink", + "ColorHues.Purple|block": "purple", + "ColorHues.Red|block": "red", + "ColorHues.Yellow|block": "yellow", + "Colors.Black|block": "black", + "Colors.Blue|block": "blue", + "Colors.Green|block": "green", + "Colors.Indigo|block": "indigo", + "Colors.Orange|block": "orange", + "Colors.Pink|block": "pink", + "Colors.Purple|block": "purple", + "Colors.Red|block": "red", + "Colors.Violet|block": "violet", + "Colors.White|block": "white", + "Colors.Yellow|block": "yellow", + "color.ColorBufferLayout.ARGB": "32bit RGB color with alpha", + "color.ColorBufferLayout.RGB": "24bit RGB color", + "color.fade|block": "fade %color=neopixel_colors|by %brightness", + "color.hsv|block": "hue %hue|sat %sat|val %val", + "color.rgb|block": "red %red|green %green|blue %blue", + "color.wellKnown|block": "%color", + "color|block": "color", + "{id:category}Color": "Color", + "{id:group}Color": "Color" +} \ No newline at end of file diff --git a/libs/color/pxt.json b/libs/color/pxt.json new file mode 100644 index 00000000000..10a8fd56d2c --- /dev/null +++ b/libs/color/pxt.json @@ -0,0 +1,4 @@ +{ + "hidden": true, + "additionalFilePath": "../../node_modules/pxt-common-packages/libs/color" +} \ No newline at end of file diff --git a/libs/core/_locales/core-jsdoc-strings.json b/libs/core/_locales/core-jsdoc-strings.json index 5249791b655..4d7055343ed 100644 --- a/libs/core/_locales/core-jsdoc-strings.json +++ b/libs/core/_locales/core-jsdoc-strings.json @@ -37,8 +37,8 @@ "Array.reduce": "Call the specified callback function for all the elements in an array. The return value of the callback function is the accumulated result, and is provided as an argument in the next call to the callback function.", "Array.reduce|param|callbackfn": "A function that accepts up to three arguments. The reduce method calls the callbackfn function one time for each element in the array.", "Array.reduce|param|initialValue": "Initial value to start the accumulation. The first call to the callbackfn function provides this value as an argument instead of an array value.", - "Array.removeAt": "Remove the element at a certain index.", - "Array.removeElement": "Remove the first occurence of an object. Returns true if removed.", + "Array.removeAt": "Remove and return the element at a certain index.", + "Array.removeElement": "Remove the first occurrence of an object. Returns true if removed.", "Array.reverse": "Reverse the elements in an array. The first array element becomes the last, and the last array element becomes the first.", "Array.set": "Store a value at a particular index", "Array.set|param|index": "the zero-based position in the list to store the value, eg: 0", @@ -64,8 +64,9 @@ "Buffer.fill": "Fill (a fragment) of the buffer with given value.", "Buffer.fromArray": "Create a new buffer initialized to bytes from given array.", "Buffer.fromArray|param|bytes": "data to initialize with", + "Buffer.fromBase64": "Create a new buffer, decoding a Base64 string", "Buffer.fromHex": "Create a new buffer, decoding a hex string", - "Buffer.fromUTF8": "Create a new buffer with UTF8-encoded string", + "Buffer.fromUTF8": "Create a new buffer from an UTF8-encoded string", "Buffer.fromUTF8|param|str": "the string to put in the buffer", "Buffer.getNumber": "Read a number in specified format from the buffer.", "Buffer.getUint8": "Reads an unsigned byte at a particular location", @@ -87,6 +88,7 @@ "Buffer.sizeOfNumberFormat": "Get the size in bytes of specified number format.", "Buffer.slice": "Return a copy of a fragment of a buffer.", "Buffer.toArray": "Read contents of buffer as an array in specified format", + "Buffer.toBase64": "Convert buffer to ASCII base64 encoding.", "Buffer.toHex": "Convert a buffer to its hexadecimal representation.", "Buffer.toString": "Convert a buffer to string assuming UTF8 encoding", "Buffer.unpack": "Reads numbers from the buffer according to the format", @@ -149,6 +151,9 @@ "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.convert": "Converts a value from one unit to another. For example, degrees to radians, fahrenheit to celsius, etc.", + "Math.convert|param|type": "The type of conversion to perform.", + "Math.convert|param|value": "The value to convert.", "Math.cos": "Returns the cosine of a number.", "Math.cos|param|x": "An angle in radians", "Math.exp": "Returns returns ``e^x``.", @@ -223,8 +228,8 @@ "String.indexOf|param|start": "optional start index for the search", "String.isEmpty": "Returns a value indicating if the string is empty", "String.length": "Returns the length of a String object.", - "String.replace": "Return the current string with the first occurence of toReplace\nreplaced with the replacer\n\n\nor a function that accepts the substring and returns the replacement string.", - "String.replaceAll": "Return the current string with each occurence of toReplace\nreplaced with the replacer\n\n\nor a function that accepts the substring and returns the replacement string.", + "String.replace": "Return the current string with the first occurrence of toReplace\nreplaced with the replacer\n\n\nor a function that accepts the substring and returns the replacement string.", + "String.replaceAll": "Return the current string with each occurrence of toReplace\nreplaced with the replacer\n\n\nor a function that accepts the substring and returns the replacement string.", "String.replaceAll|param|replacer": "either the string that replaces toReplace in the current string,", "String.replaceAll|param|toReplace": "the substring to replace in the current string", "String.replace|param|replacer": "either the string that replaces toReplace in the current string,", @@ -238,6 +243,7 @@ "String.substr|param|length": "number of characters to extract, eg: 10", "String.substr|param|start": "first character index; can be negative from counting from the end, eg:0", "String.toLowerCase": "Converts the string to lower case characters.", + "String.toUpperCase": "Converts the string to upper case characters.", "String.trim": "Return a substring of the current string with whitespace removed from both ends", "String@type": "Combine, split, and search text strings.", "StringMap": "A dictionary from string key to string values", @@ -256,7 +262,7 @@ "basic.plotLeds": "Draws an image on the LED screen.", "basic.plotLeds|param|leds": "pattern of LEDs to turn on/off", "basic.showAnimation": "Shows a sequence of LED screens as an animation.", - "basic.showAnimation|param|interval": "time in milliseconds between each redraw", + "basic.showAnimation|param|interval": "time in milliseconds between each redraw.", "basic.showAnimation|param|leds": "pattern of LEDs to turn on/off", "basic.showArrow": "Draws an arrow on the LED screen", "basic.showArrow|param|direction": "the direction of the arrow", @@ -265,13 +271,32 @@ "basic.showIcon|param|icon": "the predefined icon id", "basic.showIcon|param|interval": "the amount of time (milliseconds) to show the icon. Default is 600.", "basic.showLeds": "Draws an image on the LED screen.", - "basic.showLeds|param|interval": "time in milliseconds to pause after drawing", - "basic.showLeds|param|leds": "the pattern of LED to turn on/off", + "basic.showLeds|param|interval": "time in milliseconds to pause after drawing.", + "basic.showLeds|param|leds": "the pattern of LED to turn on/off.", "basic.showNumber": "Scroll a number on the screen. If the number fits on the screen (i.e. is a single digit), do not scroll.", "basic.showNumber|param|interval": "speed of scroll; eg: 150, 100, 200, -100", "basic.showString": "Display text on the display, one character at a time. If the string fits on the screen (i.e. is one letter), does not scroll.", "basic.showString|param|interval": "how fast to shift characters; eg: 150, 100, 200, -100", "basic.showString|param|text": "the text to scroll on the screen, eg: \"Hello!\"", + "colorHelpers.cmyk": "Converts a CMYK color into a single color number.\n*\n\n\n\n\n@returns The combined color as a single number", + "colorHelpers.cmyk|param|black": "The black component of the color, between 0 and 100", + "colorHelpers.cmyk|param|cyan": "The cyan component of the color, between 0 and 100", + "colorHelpers.cmyk|param|magenta": "The magenta component of the color, between 0 and 100", + "colorHelpers.cmyk|param|yellow": "The yellow component of the color, between 0 and 100", + "colorHelpers.hex": "Converts a hexadecimal color string into a single color number. The hexadecimal string can be in the short\n3-digit form (\"#f0a\") or the full 6-digit form (\"#ff00aa\").\n*\n\n@returns The combined color as a single number", + "colorHelpers.hex|param|hex": "A hexadecimal color string, optionally starting with \"#\" and either in 3-digit or 6-digit format", + "colorHelpers.hsl": "Converts a hue, saturation, and lightness (HSL) color into a single color number.\n*\n\n\n\n@returns The combined color as a single number", + "colorHelpers.hsl|param|hue": "The hue component of the color, between 0 and 360", + "colorHelpers.hsl|param|lightness": "The lightness component of the color, between 0 and 100", + "colorHelpers.hsl|param|saturation": "The saturation component of the color, between 0 and 100", + "colorHelpers.hsv": "Converts a hue, saturation, and value (HSV) color into a single color number.\n*\n\n\n\n@returns The combined color as a single number", + "colorHelpers.hsv|param|hue": "The hue component of the color, between 0 and 360", + "colorHelpers.hsv|param|saturation": "The saturation component of the color, between 0 and 100", + "colorHelpers.hsv|param|value": "The value component of the color, between 0 and 100", + "colorHelpers.rgb": "Converts a red, green, and blue color value into a single color number.\n*\n\n\n\n@returns The combined color as a single number", + "colorHelpers.rgb|param|blue": "The blue component of the color, between 0 and 255", + "colorHelpers.rgb|param|green": "The green component of the color, between 0 and 255", + "colorHelpers.rgb|param|red": "The red component of the color, between 0 and 255", "console": "Reading and writing data to the console output.", "console.addListener": "Adds a listener for the log messages", "console.inspect": "Convert any object or value to a string representation", @@ -285,9 +310,12 @@ "console.minPriority": "Minimum priority to send messages to listeners", "console.removeListener": "Removes a listener", "control": "Runtime and event utilities.", + "control._hardwareVersion": "Returns the major version of the microbit", + "control.allocateEventSource": "Incrementally allocates event source identifiers.", "control.allocateNotifyEvent": "Allocates the next user notification event", "control.assert": "If the condition is false, display msg on serial console, and panic with code 098.", "control.benchmark": "Runs the function and returns run time in microseconds.", + "control.compareVersion": "Given two versions, returns -1 if the first version is less than\nthe second, 1 if it's greater, and 0 if it's the same.", "control.createBuffer": "Create a new zero-initialized buffer.", "control.createBufferFromUTF8": "Create a new buffer with UTF8-encoded string", "control.createBufferFromUTF8|param|str": "the string to put in the buffer", @@ -296,17 +324,23 @@ "control.deviceName": "Make a friendly name for the device based on its serial number", "control.deviceSerialNumber": "Derive a unique, consistent serial number of this device from internal data.", "control.dmesg": "Write a message to DMESG debugging buffer.", + "control.dmesgPerfCounters": "Dump values of profiling performance counters.", "control.dmesgPtr": "Write a message and value (pointer) to DMESG debugging buffer.", + "control.enablePerfCounter": "Enable profiling for current function.", "control.eventSourceId": "Returns the value of a C++ runtime constant", "control.eventTimestamp": "Gets the timestamp of the last event executed on the bus", "control.eventValue": "Gets the value of the last event executed on the bus", "control.eventValueId": "Returns the value of a C++ runtime constant", + "control.gc": "Force GC and dump basic information about heap.", "control.gcStats": "Get various statistics about the garbage collector (GC)", + "control.heapDump": "Force GC and halt waiting for debugger to do a full heap dump.", + "control.heapSnapshot": "Record a heap snapshot to debug memory leaks.", "control.inBackground": "Schedules code that run in the background.", "control.micros": "Gets current time in microseconds. Overflows every ~18 minutes.", "control.millis": "Gets the number of milliseconds elapsed since power on.", "control.onEvent": "Registers an event handler.", "control.panic": "Display specified error code and stop the program.", + "control.profilingEnabled": "Return true if profiling is enabled in the current build.", "control.raiseEvent": "Raises an event in the event bus.", "control.raiseEvent|param|mode": "optional definition of how the event should be processed after construction (default is CREATE_AND_FIRE).", "control.raiseEvent|param|src": "ID of the MicroBit Component that generated the event e.g. MICROBIT_ID_BUTTON_A.", @@ -315,7 +349,9 @@ "control.reset": "Resets the BBC micro:bit.", "control.runInParallel": "Run other code in the parallel.", "control.runtimeWarning": "Display warning in the simulator.", + "control.setDebugFlags": "Set flags used when connecting an external debugger.", "control.simmessages.onReceived": "Registers the handler for a message on a given channel", + "control.singleSimulator": "Allow only one simulator", "control.waitForEvent": "Blocks the calling thread until the specified event is raised.", "control.waitMicros": "Blocks the current fiber for the given microseconds", "control.waitMicros|param|micros": "number of micro-seconds to wait. eg: 4", @@ -457,11 +493,12 @@ "input.setAccelerometerRange|param|range": "a value describe the maximum strengh of acceleration measured", "input.temperature": "Gets the temperature in Celsius degrees (°C).", "led": "Control of the LED screen.", + "led.barGraphToConsole": "Controls where plotbargraph prints to the console", "led.brightness": "Get the screen brightness from 0 (off) to 255 (full bright).", "led.displayMode": "Gets the current display mode", "led.enable": "Turns on or off the display", "led.fadeIn": "Fades in the screen display.", - "led.fadeIn|param|ms": "fade time in milleseconds", + "led.fadeIn|param|ms": "fade time in milliseconds", "led.fadeOut": "Fades out the screen brightness.", "led.fadeOut|param|ms": "fade time in milliseconds", "led.plot": "Turn on the specified LED using x, y coordinates (x is horizontal, y is vertical). (0,0) is upper left.", @@ -469,6 +506,7 @@ "led.plotBarGraph": "Displays a vertical bar graph based on the `value` and `high` value.\nIf `high` is 0, the chart gets adjusted automatically.", "led.plotBarGraph|param|high": "maximum value. If 0, maximum value adjusted automatically, eg: 0", "led.plotBarGraph|param|value": "current value to plot", + "led.plotBarGraph|param|valueToConsole": "if true, prints value to the serial port", "led.plotBrightness": "Turn on the specified LED with specific brightness using x, y coordinates (x is horizontal, y is vertical). (0,0) is upper left.", "led.plotBrightness|param|brightness": "the brightness from 0 (off) to 255 (bright), eg:255", "led.plotBrightness|param|x": "the horizontal coordinate of the LED starting at 0", @@ -497,6 +535,8 @@ "light.sendWS2812Buffer": "Sends a color buffer to a light strip", "light.sendWS2812BufferWithBrightness": "Sends a color buffer to a light strip", "light.setMode": "Sets the light mode of a pin", + "loops.everyInterval": "Repeats the code forever in the background.\nAfter each iteration, allows other codes to run for a set duration\nso that it runs on a timer", + "loops.everyInterval|param|interval": "time (in ms) to wait between each iteration of the action.", "msgpack.packNumberArray": "Pack a number array into a buffer.", "msgpack.packNumberArray|param|nums": "the numbers to be packed", "msgpack.unpackNumberArray": "Unpacks a buffer into a number array.", @@ -504,18 +544,51 @@ "music.beat": "Returns the duration of a beat in milli-seconds", "music.beginMelody": "Use startMelody instead", "music.builtInMelody": "Gets the melody array of a built-in melody.", + "music.builtInPlayableMelody": "Gets the melody array of a built-in melody.", + "music.builtInPlayableMelody|param|melody": "the melody name", + "music.builtinPlayableSoundEffect": "Get the sound expression string for a built-in sound effect.", + "music.builtinPlayableSoundEffect|param|soundExpression": "a sound expression for a built-in sound effect", + "music.builtinSoundEffect": "Get the sound expression string for a built-in a sound effect.", + "music.builtinSoundEffect|param|soundExpression": "a sound expression for a built-in sound effect", "music.changeTempoBy": "Change the tempo by the specified amount", "music.changeTempoBy|param|bpm": "The change in beats per minute to the tempo, eg: 20", + "music.createSoundEffect": "Create a sound expression from a set of sound effect parameters.", + "music.createSoundEffect|param|duration": "the amount of time in milliseconds (ms) that sound will play for", + "music.createSoundEffect|param|effect": "the effect to apply to the waveform or volume", + "music.createSoundEffect|param|endFrequency": "ending frequency for the sound effect waveform", + "music.createSoundEffect|param|endVolume": "ending volume of the sound, or ending amplitude", + "music.createSoundEffect|param|interpolation": "interpolation method for frequency scaling", + "music.createSoundEffect|param|startFrequency": "starting frequency for the sound effect waveform", + "music.createSoundEffect|param|startVolume": "starting volume of the sound, or starting amplitude", + "music.createSoundEffect|param|waveShape": "waveform of the sound effect", + "music.createSoundExpression": "Create a sound expression from a set of sound effect parameters.", + "music.createSoundExpression|param|duration": "the amount of time in milliseconds (ms) that sound will play for", + "music.createSoundExpression|param|effect": "the effect to apply to the waveform or volume", + "music.createSoundExpression|param|endFrequency": "ending frequency for the sound effect waveform", + "music.createSoundExpression|param|endVolume": "ending volume of the sound, or ending amplitude", + "music.createSoundExpression|param|interpolation": "interpolation method for frequency scaling", + "music.createSoundExpression|param|startFrequency": "starting frequency for the sound effect waveform", + "music.createSoundExpression|param|startVolume": "starting volume of the sound, or starting amplitude", + "music.createSoundExpression|param|waveShape": "waveform of the sound effect", + "music.getFrequencyForNote": "Converts an octave and note offset into an integer frequency.\nReturns 0 if the note is out of range.\n* @param octave The octave of the note (1 - 8)\n\n@returns A frequency in HZ or 0 if out of range", + "music.getFrequencyForNote|param|note": "The offset of the note within the octave", + "music.isSoundPlaying": "Check whether any sound is being played, no matter the source", "music.melodyEditor": "Create a melody with the melody editor.", "music.noteFrequency": "Gets the frequency of a note.", "music.noteFrequency|param|name": "the note name", "music.onEvent": "Registers code to run on various melody events", + "music.play": "Play a song, melody, or other sound. The music plays until finished or can play as a\nbackground task.", "music.playMelody": "Play a melody from the melody editor.", - "music.playMelody|param|melody": "- string of up to eight notes [C D E F G A B C5] or rests [-] separated by spaces, which will be played one at a time, ex: \"E D G F B A C5 B \"", - "music.playMelody|param|tempo": "- number in beats per minute (bpm), dictating how long each note will play for", + "music.playMelody|param|melody": "string of up to eight notes [C D E F G A B C5] or rests [-] separated by spaces, which will be played one at a time, ex: \"E D G F B A C5 B \"", + "music.playMelody|param|tempo": "number in beats per minute (bpm), dictating how long each note will play for", + "music.playSoundEffect": "Play a sound effect from a sound expression string.", + "music.playSoundEffect|param|mode": "the play mode, play until done or in the background", + "music.playSoundEffect|param|sound": "the sound expression string", "music.playTone": "Plays a tone through pin ``P0`` for the given duration.", "music.playTone|param|frequency": "pitch of the tone to play in Hertz (Hz), eg: Note.C", "music.playTone|param|ms": "tone duration in milliseconds (ms)", + "music.play|param|playbackMode": "play the song or melody until it's finished or as background task", + "music.play|param|toPlay": "the song or melody to play", "music.rest": "Rests (plays nothing) for a specified time through pin ``P0``.", "music.rest|param|ms": "rest duration in milliseconds (ms)", "music.ringTone": "Plays a tone through pin ``P0``.", @@ -534,7 +607,13 @@ "music.stopAllSounds": "Stop all sounds and melodies currently playing.", "music.stopMelody": "Stops the melodies", "music.stopMelody|param|options": "which melody to stop", + "music.stringPlayable": "Play a melody from the melody editor", + "music.stringPlayable|param|bpm": "number in beats per minute dictating how long each note will play", + "music.stringPlayable|param|melody": "string of up to eight notes [C D E F G A B C5] or rests [-] separated by spaces, which will be played one at a time, ex: \"E D G F B A C5 B \"", "music.tempo": "Returns the tempo in beats per minute. Tempo is the speed (bpm = beats per minute) at which notes play. The larger the tempo value, the faster the notes will play.", + "music.tonePlayable": "Plays a tone through pin ``P0`` for the given duration.", + "music.tonePlayable|param|duration": "tone duration in milliseconds (ms)", + "music.tonePlayable|param|note": "pitch of the tone to play in Hertz (Hz).", "music.volume": "Returns the current output volume of the sound synthesizer.", "parseFloat": "Convert a string to a number.", "parseInt": "Convert a string to an integer.\n\n\nIf this argument is not supplied, strings with a prefix of '0x' are considered hexadecimal.\nAll other strings are considered decimal.", @@ -565,14 +644,19 @@ "pins.P7": "Pin P7", "pins.P8": "Pin P8", "pins.P9": "Pin P9", - "pins.analogPitch": "Emit a plse-width modulation (PWM) signal to the current pitch pin. Use `analog set pitch pin` to define the pitch pin.", + "pins._analogPin": "Returns the value of a C++ runtime constant", + "pins._analogPinShadow": "Returns the value of a C++ runtime constant", + "pins._analogReadWritePinShadow": "Returns the value of a C++ runtime constant", + "pins._digitalPin": "Returns the value of a C++ runtime constant", + "pins._digitalPinShadow": "Returns the value of a C++ runtime constant", + "pins.analogPitch": "Send a pulse-width modulation (PWM) signal to the current pitch pin. Use `analog set pitch pin` to define the pitch pin.", "pins.analogPitchVolume": "Gets the volume the pitch pin from 0..255", "pins.analogPitch|param|frequency": "frequency to modulate in Hz.", - "pins.analogPitch|param|ms": "duration of the pitch in milli seconds.", + "pins.analogPitch|param|ms": "duration of the pitch in milliseconds.", "pins.analogReadPin": "Read the connector value as analog, that is, as a value comprised between 0 and 1023.", "pins.analogReadPin|param|name": "pin to write to, eg: AnalogPin.P0", "pins.analogSetPeriod": "Configure the pulse-width modulation (PWM) period of the analog output in microseconds.\nIf this pin is not configured as an analog output (using `analog write pin`), the operation has no effect.", - "pins.analogSetPeriod|param|micros": "period in micro seconds. eg:20000", + "pins.analogSetPeriod|param|micros": "period in microseconds. eg:20000", "pins.analogSetPeriod|param|name": "analog pin to set period to, eg: AnalogPin.P0", "pins.analogSetPitchPin": "Set the pin used when using analog pitch or music.", "pins.analogSetPitchPin|param|name": "pin to modulate pitch from", @@ -608,18 +692,19 @@ "pins.pushButton": "Mounts a push button on the given pin", "pins.servoSetContinuous": "Specifies that a continuous servo is connected.", "pins.servoSetPulse": "Configure the IO pin as an analog/pwm output and set a pulse width. The period is 20 ms period and the pulse width is set based on the value given in **microseconds** or `1/1000` milliseconds.", - "pins.servoSetPulse|param|micros": "pulse duration in micro seconds, eg:1500", + "pins.servoSetPulse|param|micros": "pulse duration in microseconds, eg:1500", "pins.servoSetPulse|param|name": "pin name", "pins.servoWritePin": "Write 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).", "pins.servoWritePin|param|name": "pin to write to, eg: AnalogPin.P0", "pins.servoWritePin|param|value": "angle or rotation speed, eg:180,90,0", "pins.setAudioPin": "Set the pin used when producing sounds and melodies. Default is P0.", + "pins.setAudioPinEnabled": "Sets whether or not audio will be output using a pin on the edge\nconnector.", "pins.setAudioPin|param|name": "pin to modulate pitch from", "pins.setEvents": "Configure the events emitted by this pin. Events can be subscribed to\nusing ``control.onEvent()``.", "pins.setEvents|param|name": "pin to set the event mode on, eg: DigitalPin.P0", "pins.setEvents|param|type": "the type of events for this pin to emit, eg: PinEventType.Edge", "pins.setMatrixWidth": "Set the matrix width for Neopixel strip (already assigned to a pin).\nShould be used in conjunction with `set matrix width` from Neopixel package.", - "pins.setPull": "Configure the pull directiion of of a pin.", + "pins.setPull": "Configure the pull direction of of a pin.", "pins.setPull|param|name": "pin to set the pull mode on, eg: DigitalPin.P0", "pins.setPull|param|pull": "one of the mbed pull configurations, eg: PinPullMode.PullUp", "pins.spiFormat": "Set the SPI bits and mode", @@ -644,7 +729,7 @@ "serial.delimiters": "Return the corresponding delimiter string", "serial.onDataReceived": "Register an event to be fired when one of the delimiter is matched.", "serial.onDataReceived|param|delimiters": "the characters to match received characters against.", - "serial.readBuffer": "Read multiple characters from the receive buffer. \nIf length is positive, pauses until enough characters are present.", + "serial.readBuffer": "Read multiple characters from the receive buffer.\nIf length is positive, pauses until enough characters are present.", "serial.readBuffer|param|length": "default buffer length", "serial.readLine": "Read a line of text from the serial port.", "serial.readString": "Read the buffered received data as a string", @@ -663,6 +748,7 @@ "serial.setWriteLinePadding": "Sets the padding length for lines sent with \"write line\".", "serial.setWriteLinePadding|param|length": "the number of bytes alignment, eg: 0", "serial.writeBuffer": "Send a buffer through serial connection", + "serial.writeDmesg": "Send DMESG debug buffer over serial.", "serial.writeLine": "Print a line of text to the serial port", "serial.writeNumber": "Print a numeric value to the serial port", "serial.writeNumbers": "Print an array of numeric values as CSV to the serial port", diff --git a/libs/core/_locales/core-strings.json b/libs/core/_locales/core-strings.json index 0f6b1fd6bf4..8da45ef6a05 100644 --- a/libs/core/_locales/core-strings.json +++ b/libs/core/_locales/core-strings.json @@ -7,19 +7,6 @@ "AcceleratorRange.OneG|block": "1g", "AcceleratorRange.TwoG": "The accelerator measures forces up to 2 gravity", "AcceleratorRange.TwoG|block": "2g", - "AnalogPin.P11|block": "P11 (write only)", - "AnalogPin.P12|block": "P12 (write only)", - "AnalogPin.P13|block": "P13 (write only)", - "AnalogPin.P14|block": "P14 (write only)", - "AnalogPin.P15|block": "P15 (write only)", - "AnalogPin.P16|block": "P16 (write only)", - "AnalogPin.P19|block": "P19 (write only)", - "AnalogPin.P20|block": "P20 (write only)", - "AnalogPin.P5|block": "P5 (write only)", - "AnalogPin.P6|block": "P6 (write only)", - "AnalogPin.P7|block": "P7 (write only)", - "AnalogPin.P8|block": "P8 (write only)", - "AnalogPin.P9|block": "P9 (write only)", "Array._pickRandom|block": "get random value from %list", "Array._popStatement|block": "remove last value from %list", "Array._removeAtStatement|block": "%list| remove value at %index", @@ -56,9 +43,13 @@ "BaudRate.BaudRate9600|block": "9600", "BeatFraction.Breve|block": "4", "BeatFraction.Double|block": "2", + "BeatFraction.Eighth|ariaLabel": "one eighth", "BeatFraction.Eighth|block": "1/8", + "BeatFraction.Half|ariaLabel": "one half", "BeatFraction.Half|block": "1/2", + "BeatFraction.Quarter|ariaLabel": "one quarter", "BeatFraction.Quarter|block": "1/4", + "BeatFraction.Sixteenth|ariaLabel": "one sixteenth", "BeatFraction.Sixteenth|block": "1/16", "BeatFraction.Whole|block": "1", "Buffer|block": "Buffer", @@ -114,6 +105,7 @@ "IconNames.Cow|block": "cow", "IconNames.Diamond|block": "diamond", "IconNames.Duck|block": "duck", + "IconNames.EighthNote|block": "eighth note", "IconNames.EigthNote|block": "eigth note", "IconNames.Fabulous|block": "fabulous", "IconNames.Ghost|block": "ghost", @@ -148,13 +140,26 @@ "IconNames.Yes|block": "yes", "Image.scrollImage|block": "scroll image %sprite(myImage)|with offset %frameoffset|and interval (ms) %delay", "Image.showImage|block": "show image %sprite(myImage)|at offset %offset ||and interval (ms) %interval", + "InterpolationCurve.Curve|block": "curve", + "InterpolationCurve.Linear|block": "linear", + "InterpolationCurve.Logarithmic|block": "logarithmic", "JSON|block": "JSON", "LedSpriteProperty.Blink|block": "blink", "LedSpriteProperty.Brightness|block": "brightness", "LedSpriteProperty.Direction|block": "direction", "LedSpriteProperty.X|block": "x", "LedSpriteProperty.Y|block": "y", + "Math.E|block": "e", + "Math.LN10|block": "ln(10)", + "Math.LN2|block": "ln(2)", + "Math.LOG10E|block": "log₁₀(e)", + "Math.LOG2E|block": "log₂(e)", + "Math.PI|block": "Ī€", + "Math.SQRT1_2|block": "√ÂŊ", + "Math.SQRT2|block": "√2", + "Math._constant|block": "$MEMBER", "Math.constrain|block": "constrain %value|between %low|and %high", + "Math.convert|block": "convert $value|from $type", "Math.map|block": "map %value|from low %fromLow|high %fromHigh|to low %toLow|high %toHigh", "Math.randomBoolean|block": "pick random true or false", "Math.randomRange|block": "pick random %min|to %limit", @@ -243,7 +248,14 @@ "Rotation.Roll|block": "roll", "SoundExpression.playUntilDone|block": "play sound $this until done", "SoundExpression.play|block": "play sound $this", + "SoundExpressionEffect.None|block": "none", + "SoundExpressionEffect.Tremolo|block": "tremolo", + "SoundExpressionEffect.Vibrato|block": "vibrato", + "SoundExpressionEffect.Warble|block": "warble", + "SoundExpressionPlayMode.InBackground|block": "in background", + "SoundExpressionPlayMode.UntilDone|block": "until done", "String.charAt|block": "char from %this=text|at %pos", + "String.charCodeAt|block": "char code from $this=text|at $index", "String.compare|block": "compare %this=text| to %that", "String.fromCharCode|block": "text from char code %code", "String.includes|block": "%this=text|includes %searchValue", @@ -251,7 +263,7 @@ "String.isEmpty|block": "%this=text| is empty", "String.length|block": "length of %VALUE", "String.split|block": "split %this=text|at %separator", - "String.substr|block": "substring of %this=text|from %start|of length %length", + "String.substr|block": "substring of $this|from $start||of length $length", "String|block": "String", "TouchButtonEvent.LongPressed|block": "long pressed", "TouchButtonEvent.Pressed|block": "pressed", @@ -263,6 +275,15 @@ "TouchTarget.P2|block": "P2", "TouchTargetMode.Capacitive|block": "capacitive", "TouchTargetMode.Resistive|block": "resistive", + "UnitConversion.CelsiusToFahrenheit|block": "celsius to fahrenheit", + "UnitConversion.DegreesToRadians|block": "degrees to radians", + "UnitConversion.FahrenheitToCelsius|block": "fahrenheit to celsius", + "UnitConversion.RadiansToDegrees|block": "radians to degrees", + "WaveShape.Noise|block": "noise", + "WaveShape.Sawtooth|block": "sawtooth", + "WaveShape.Sine|block": "sine", + "WaveShape.Square|block": "square", + "WaveShape.Triangle|block": "triangle", "_py|block": "_py", "basic.clearScreen|block": "clear screen", "basic.forever|block": "forever", @@ -272,7 +293,9 @@ "basic.showLeds|block": "show leds", "basic.showNumber|block": "show|number %number", "basic.showString|block": "show|string %text", + "basic.showString|param|text|defl": "Hello!", "basic|block": "basic", + "colorHelpers|block": "colorHelpers", "console|block": "console", "control.deviceName|block": "device name", "control.deviceSerialNumber|block": "device serial number", @@ -342,7 +365,7 @@ "input|block": "input", "led.brightness|block": "brightness", "led.enable|block": "led enable %on", - "led.plotBarGraph|block": "plot bar graph of %value up to %high", + "led.plotBarGraph|block": "plot bar graph of $value up to $high|| serial write $valueToConsole", "led.plotBrightness|block": "plot|x %x|y %y|brightness %brightness", "led.plot|block": "plot|x %x|y %y", "led.pointBrightness|block": "point|x %x|y %y brightness", @@ -352,18 +375,32 @@ "led.stopAnimation|block": "stop animation", "led.toggle|block": "toggle|x %x|y %y", "led.unplot|block": "unplot|x %x|y %y", - "led|block": "led", + "led|block": "LED", "light|block": "light", + "loops.everyInterval|block": "every $interval ms", + "loops|block": "loops", "msgpack|block": "msgpack", + "music.PlaybackMode.InBackground|block": "in background", + "music.PlaybackMode.LoopingInBackground|block": "looping in background", + "music.PlaybackMode.UntilDone|block": "until done", + "music._playDefaultBackground|block": "play $toPlay $playbackMode", "music.beat|block": "%fraction|beat", "music.builtInMelody|block": "%melody", + "music.builtInPlayableMelody|block": "melody|$melody", + "music.builtinPlayableSoundEffect|block": "$soundExpression", + "music.builtinSoundEffect|block": "$soundExpression", "music.changeTempoBy|block": "change tempo by (bpm)|%value", + "music.createSoundEffect|block": "$waveShape|| start frequency $startFrequency end frequency $endFrequency duration $duration start volume $startVolume end volume $endVolume effect $effect interpolation $interpolation", + "music.createSoundExpression|block": "$waveShape|| start frequency $startFrequency end frequency $endFrequency duration $duration start volume $startVolume end volume $endVolume effect $effect interpolation $interpolation", + "music.isSoundPlaying|block": "sound is playing", "music.melodyEditor|block": "$melody", "music.noteFrequency|block": "%name", "music.onEvent|block": "music on %value", "music.playMelody|block": "play melody $melody at tempo $tempo|(bpm)", + "music.playSoundEffect|block": "play sound $sound $mode", "music.playTone|block": "play|tone %note=device_note|for %duration=device_beat", - "music.rest|block": "rest(ms)|%duration=device_beat", + "music.play|block": "play $toPlay $playbackMode", + "music.rest|block": "rest for |%duration=device_beat", "music.ringTone|block": "ring tone (Hz)|%note=device_note", "music.setBuiltInSpeakerEnabled|block": "set built-in speaker $enabled", "music.setTempo|block": "set tempo to (bpm)|%value", @@ -371,11 +408,20 @@ "music.startMelody|block": "start melody %melody=device_builtin_melody| repeating %options", "music.stopAllSounds|block": "stop all sounds", "music.stopMelody|block": "stop melody $options", + "music.stringPlayable|block": "melody $melody at tempo $bpm|(bpm)", "music.tempo|block": "tempo (bpm)", + "music.tonePlayable|block": "tone $note for $duration", "music.volume|block": "volume", "music|block": "music", "parseFloat|block": "parse to number %text", + "parseFloat|param|text|defl": "123", "parseInt|block": "parse to integer %text", + "parseInt|param|text|defl": "123", + "pins._analogPinShadow|block": "$pin", + "pins._analogPin|block": "analog pin $pin", + "pins._analogReadWritePinShadow|block": "$pin", + "pins._digitalPinShadow|block": "$pin", + "pins._digitalPin|block": "digital pin $pin", "pins.analogPitchVolume|block": "analog pitch volume", "pins.analogPitch|block": "analog pitch %frequency|for (ms) %ms", "pins.analogReadPin|block": "analog read|pin %name", @@ -393,6 +439,7 @@ "pins.pulseIn|block": "pulse in (Âĩs)|pin %name|pulsed %value", "pins.servoSetPulse|block": "servo set pulse|pin %value|to (Âĩs) %micros", "pins.servoWritePin|block": "servo write|pin %name|to %value", + "pins.setAudioPinEnabled|block": "set audio pin enabled $enabled", "pins.setAudioPin|block": "set audio pin $name", "pins.setEvents|block": "set pin %pin|to emit %type|events", "pins.setMatrixWidth|block": "neopixel matrix width|pin %pin %width", @@ -422,6 +469,7 @@ "serial.writeNumber|block": "serial|write number %value", "serial.writeString|block": "serial|write string %text", "serial.writeValue|block": "serial|write value %name|= %value", + "serial.writeValue|param|name|defl": "x", "serial|block": "serial", "soundExpression.giggle|block": "{id:soundexpression}giggle", "soundExpression.happy|block": "{id:soundexpression}happy", @@ -440,6 +488,7 @@ "{id:category}Basic": "Basic", "{id:category}Boolean": "Boolean", "{id:category}Buffer": "Buffer", + "{id:category}ColorHelpers": "ColorHelpers", "{id:category}Console": "Console", "{id:category}Control": "Control", "{id:category}DigitalInOutPin": "DigitalInOutPin", @@ -453,6 +502,7 @@ "{id:category}JSON": "JSON", "{id:category}Led": "Led", "{id:category}Light": "Light", + "{id:category}Loops": "Loops", "{id:category}Math": "Math", "{id:category}MicrobitPin": "MicrobitPin", "{id:category}Msgpack": "Msgpack", @@ -467,11 +517,16 @@ "{id:category}Text": "Text", "{id:category}_py": "_py", "{id:group}Configuration": "Configuration", + "{id:group}I2C": "I2C", "{id:group}Melody": "Melody", "{id:group}Melody Advanced": "Melody Advanced", "{id:group}Modify": "Modify", "{id:group}Operations": "Operations", + "{id:group}Pins": "Pins", + "{id:group}Pulse": "Pulse", "{id:group}Read": "Read", + "{id:group}SPI": "SPI", + "{id:group}Servo": "Servo", "{id:group}Tempo": "Tempo", "{id:group}Tone": "Tone", "{id:group}Volume": "Volume", diff --git a/libs/core/basic.cpp b/libs/core/basic.cpp index 1e2017753c7..4426305952d 100644 --- a/libs/core/basic.cpp +++ b/libs/core/basic.cpp @@ -6,11 +6,10 @@ */ //% color=#1E90FF weight=116 icon="\uf00a" namespace basic { - /** * Draws an image on the LED screen. - * @param leds the pattern of LED to turn on/off - * @param interval time in milliseconds to pause after drawing + * @param leds the pattern of LED to turn on/off. + * @param interval time in milliseconds to pause after drawing. */ //% help=basic/show-leds //% weight=95 blockGap=8 @@ -30,6 +29,7 @@ namespace basic { //% help=basic/show-string //% weight=87 blockGap=16 //% block="show|string %text" + //% text.label="value" //% async //% blockId=device_print_message //% parts="ledmatrix" @@ -61,7 +61,7 @@ namespace basic { /** * Shows a sequence of LED screens as an animation. * @param leds pattern of LEDs to turn on/off - * @param interval time in milliseconds between each redraw + * @param interval time in milliseconds between each redraw. */ //% help=basic/show-animation imageLiteral=1 async //% parts="ledmatrix" @@ -96,6 +96,7 @@ namespace basic { */ //% help=basic/pause weight=54 //% async block="pause (ms) %pause" blockGap=16 + //% ms.label="value" //% blockId=device_pause icon="\uf110" //% pause.shadow=timePicker void pause(int ms) { diff --git a/libs/core/basic.ts b/libs/core/basic.ts index 94287a58807..51395c34e2e 100644 --- a/libs/core/basic.ts +++ b/libs/core/basic.ts @@ -7,10 +7,14 @@ namespace basic { //% help=basic/show-number //% weight=96 //% blockId=device_show_number block="show|number %number" blockGap=8 + //% value.label="value" //% async //% parts="ledmatrix" interval.defl=150 export function showNumber(value: number, interval?: number) { - showString(Math.roundWithPrecision(value, 2).toString(), interval); + if (isNaN(value)) + showString("?") + else + showString(Math.roundWithPrecision(value, 2).toString(), interval); } } @@ -19,6 +23,7 @@ namespace basic { * @param ms how long to pause for, eg: 100, 200, 500, 1000, 2000 */ function pause(ms: number): void { + if (isNaN(ms)) ms = 20 basic.pause(ms); } diff --git a/libs/core/blocks-test/pins.blocks b/libs/core/blocks-test/pins.blocks old mode 100644 new mode 100755 index 9e01d01cd86..e203e7814e6 --- a/libs/core/blocks-test/pins.blocks +++ b/libs/core/blocks-test/pins.blocks @@ -1,209 +1,265 @@ - - - DigitalPin.P5 - PulseValue.Low - - - AnalogPin.P9 - - - 5 - - - - - AnalogPin.P10 - - - 20000 - - - - - DigitalPin.P6 - - - 5 - - - - - AnalogPin.P13 - - - 5 - - - - - AnalogPin.P8 - - - 1500 - - - - - 0 - - - NumberFormat.Int8BE - - - 0 - - - - - FALSE - - - - - - - 0 - - - DigitalPin.P9 - PulseValue.Low - - - - - 1023 - - - - - - 0 - - - - - 0 - - - - - - - 4 - - - - - - - NumberFormat.Int16BE - - - 0 - - - DigitalPin.P9 - - - - - 0 - - - AnalogPin.P9 - - - - - FALSE - - - - - DigitalPin.P11 - DigitalPin.P9 - DigitalPin.P10 - - - DigitalPin.P9 - PinPullMode.PullDown - - - - - 0 - - - - - 0 - - - - - DigitalPin.P8 - PinEventType.Touch - - - AnalogPin.P9 - - - - - 8 - - - - - 3 - - - - - - - 8 - - - - - 3 - - - - - - - 1000000 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + DigitalPin.P10 + PulseValue.Low + + + + + AnalogPin.P9 + + + + + 5 + + + + + + + AnalogPin.P10 + + + + + 20000 + + + + + + + DigitalPin.P6 + + + + + 5 + + + + + + + AnalogPin.P13 + + + + + 5 + + + + + + + AnalogPin.P8 + + + + + 1500 + + + + + 0 + + + NumberFormat.Int8BE + + + 0 + + + + + FALSE + + + + + + + 0 + + + + + DigitalPin.P9 + + + PulseValue.Low + + + + + 1023 + + + + + + 0 + + + + + 0 + + + + + + + 4 + + + + + + + NumberFormat.Int16BE + + + 0 + + + + + DigitalPin.P9 + + + + + + + 0 + + + + + AnalogPin.P9 + + + + + + + FALSE + + + + + + + DigitalPin.P11 + + + + + DigitalPin.P9 + + + + + DigitalPin.P10 + + + + + + + DigitalPin.P9 + + + PinPullMode.PullDown + + + + + 0 + + + + + 0 + + + + + + + DigitalPin.P8 + + + PinEventType.Touch + + + + + AnalogPin.P9 + + + + + + + 8 + + + + + 3 + + + + + + + 8 + + + + + 3 + + + + + + + 1000000 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/libs/core/blocks-test/test.blocks b/libs/core/blocks-test/test.blocks old mode 100644 new mode 100755 index d1e1be1ea02..9f4cf6df67a --- a/libs/core/blocks-test/test.blocks +++ b/libs/core/blocks-test/test.blocks @@ -150,31 +150,51 @@ DisplayMode.Greyscale - AnalogPin.P4 + + + AnalogPin.P4 + + - + 1023 - AnalogPin.P13 + + + AnalogPin.P13 + + - DigitalPin.P10 + + + DigitalPin.P10 + + - + 0 - DigitalPin.P15 + + + DigitalPin.P15 + + + + AnalogPin.P9 + + 1234 @@ -218,10 +238,14 @@ - AnalogPin.P20 + + + AnalogPin.P20 + + - + 180 @@ -230,7 +254,11 @@ - AnalogPin.P14 + + + AnalogPin.P14 + + 1500 @@ -419,7 +447,7 @@ - + 255 @@ -428,7 +456,7 @@ - + 255 @@ -544,7 +572,11 @@ - DigitalPin.P0 + + + DigitalPin.P0 + + PinEventType.Touch @@ -555,10 +587,18 @@ - AnalogPin.P2 + + + AnalogPin.P2 + + - DigitalPin.P2 + + + DigitalPin.P2 + + PinPullMode.PullDown @@ -574,9 +614,21 @@ - DigitalPin.P9 - DigitalPin.P14 - DigitalPin.P16 + + + DigitalPin.P9 + + + + + DigitalPin.P14 + + + + + DigitalPin.P16 + + diff --git a/libs/core/codal.cpp b/libs/core/codal.cpp index 877b403217c..dda26bcd3b2 100644 --- a/libs/core/codal.cpp +++ b/libs/core/codal.cpp @@ -24,7 +24,11 @@ int list_fibers(Fiber **dest) { #endif extern "C" void target_panic(int error_code) { -#if !MICROBIT_CODAL +#if MICROBIT_CODAL + target_disable_irq(); + DMESG("PANIC %d", error_code); + pxt::dumpDmesg(); +#else // wait for serial to flush sleep_us(300000); #endif @@ -46,7 +50,6 @@ MicroBitEvent lastEvent; bool serialLoggingDisabled; void platform_init() { - microbit_seed_random(); int seed = microbit_random(0x7fffffff); DMESG("random seed: %d", seed); seedRandom(seed); @@ -79,24 +82,19 @@ void deleteListener(MicroBitListener *l) { } static void initCodal() { - // TODO!!! -#ifndef MICROBIT_CODAL uBit.messageBus.setListenerDeletionCallback(deleteListener); -#endif // repeat error 4 times and restart as needed microbit_panic_timeout(4); } -void dumpDmesg() {} - // --------------------------------------------------------------------------- // An adapter for the API expected by the run-time. // --------------------------------------------------------------------------- void registerWithDal(int id, int event, Action a, int flags) { uBit.messageBus.ignore(id, event, dispatchForeground); - uBit.messageBus.listen(id, event, dispatchForeground, a); + uBit.messageBus.listen(id, event, dispatchForeground, a, (uint16_t) flags); incr(a); registerGCPtr(a); } diff --git a/libs/core/control.cpp b/libs/core/control.cpp index e7e5188f44b..36c078f1686 100644 --- a/libs/core/control.cpp +++ b/libs/core/control.cpp @@ -16,6 +16,8 @@ enum class EventCreationMode { CreateAndFire = CREATE_AND_FIRE, }; +const char *MICROBIT_BOARD_VERSION[3] = { "2.0", "2.2", "2.X" }; + // note the trailing '_' in names - otherwise we get conflict with the pre-processor // this trailing underscore is removed by enums.d.ts generation process @@ -256,6 +258,7 @@ namespace control { */ //% help=control/wait-for-event async //% blockId=control_wait_for_event block="wait for event|from %src|with value %value" + //% src.label="source" value.label="value" void waitForEvent(int src, int value) { pxt::waitForEvent(src, value); } @@ -269,12 +272,20 @@ namespace control { microbit_reset(); } + + + + //% + void singleSimulator() { } + /** * Blocks the current fiber for the given microseconds * @param micros number of micro-seconds to wait. eg: 4 */ - //% help=control/wait-micros weight=29 + //% help=control/wait-micros weight=29 async //% blockId="control_wait_us" block="wait (Âĩs)%micros" + //% micros.label="microseconds" + //% micros.min=0 micros.max=6000 void waitMicros(int micros) { sleep_us(micros); } @@ -286,6 +297,7 @@ namespace control { * @param mode optional definition of how the event should be processed after construction (default is CREATE_AND_FIRE). */ //% weight=21 blockGap=12 blockId="control_raise_event" block="raise event|from source %src=control_event_source_id|with value %value=control_event_value_id" blockExternalInputs=1 + //% src.label="source" value.label="value" //% help=control/raise-event //% mode.defl=CREATE_AND_FIRE void raiseEvent(int src, int value, EventCreationMode mode) { @@ -296,6 +308,7 @@ namespace control { * Registers an event handler. */ //% weight=20 blockGap=8 blockId="control_on_event" block="on event|from %src=control_event_source_id|with value %value=control_event_value_id" + //% src.label="source" value.label="value" //% help=control/on-event //% blockExternalInputs=1 void onEvent(int src, int value, Action handler, int flags = 0) { @@ -333,6 +346,33 @@ namespace control { return mkString(microbit_friendly_name(), -1); } + /** + * Returns the major version of the microbit + */ + //% help=control/hardware-version + String _hardwareVersion() { + #if MICROBIT_CODAL + MicroBitVersion v = uBit.power.getVersion(); + int versionIdx; + switch (v.board) { + case 0x9903: + case 0x9904: + versionIdx = 0; + break; + case 0x9905: + case 0x9906: + versionIdx = 1; + break; + default: + versionIdx = 2; + break; + } + return mkString(MICROBIT_BOARD_VERSION[versionIdx], -1); + #else + return mkString("1.X", 1); + #endif + } + /** * Derive a unique, consistent serial number of this device from internal data. */ diff --git a/libs/core/control.ts b/libs/core/control.ts index 84031de93f2..c85f8c771d5 100644 --- a/libs/core/control.ts +++ b/libs/core/control.ts @@ -16,6 +16,14 @@ namespace control { export function runInBackground(a: () => void) { control.inBackground(a); } + + /** + * Allow only one simulator + */ + //% shim=control::singleSimulator + export function singleSimulator() { + + } /** * Returns the value of a C++ runtime constant @@ -105,6 +113,14 @@ namespace control { panic(108) } + let _evSource = 0x8000 + /** + * Incrementally allocates event source identifiers. + */ + export function allocateEventSource() { + return ++_evSource + } + /** * Display warning in the simulator. */ @@ -132,6 +148,51 @@ namespace control { t += 0x3fffffff return t } + + /** + * Given two versions, returns -1 if the first version is less than + * the second, 1 if it's greater, and 0 if it's the same. + */ + //% + export function compareVersion(version1: string, version2: string): number { + let v1Arr = version1.split("."); + let v2Arr = version2.split("."); + + v1Arr = v1Arr.map((str) => { + if (str.includes("x") || str.includes("X")) { + return "0" + } else { + return str; + } + }) + v2Arr = v2Arr.map((str) => { + if (str.includes("x") || str.includes("X")) { + return "0" + } else { + return str; + } + }) + + for (let i = v1Arr.length; i < Math.max(v1Arr.length, v2Arr.length); i++) { + v1Arr.push("0"); + } + + for (let i = v2Arr.length; i < Math.max(v1Arr.length, v2Arr.length); i++) { + v2Arr.push("0"); + } + + for (let i = 0; i < v1Arr.length; i++) { + if (parseInt(v1Arr[i]) != parseInt(v2Arr[i])) { + return parseInt(v1Arr[i]) - parseInt(v2Arr[i]); + } + } + return 0; + } + + //% shim=control::_hardwareVersion + export function hardwareVersion(): string { + return "2.0"; + } } /** @@ -140,6 +201,7 @@ namespace control { */ //% help=text/convert-to-text weight=1 //% block="convert $value=math_number to text" +//% value.label="value" //% blockId=variable_to_text blockNamespace="text" function convertToText(value: any): string { return "" + value; diff --git a/libs/core/dal.d.ts b/libs/core/dal.d.ts index acc111a4a7a..60821806171 100644 --- a/libs/core/dal.d.ts +++ b/libs/core/dal.d.ts @@ -1,137 +1,5 @@ // Auto-generated. Do not edit. declare const enum DAL { - // /libraries/codal-core/inc/JACDAC/JACDAC.h - JD_STARTED = 2, - JD_SERVICE_ARRAY_SIZE = 20, - // /libraries/codal-core/inc/JACDAC/JDPhysicalLayer.h - JD_VERSION = 0, - JD_BYTE_AT_125KBAUD = 80, - JD_MIN_INTERLODATA_SPACING = 40, - JD_SERIAL_MAX_BUFFERS = 10, - JD_SERIAL_MAX_SERVICE_NUMBER = 15, - JD_SERIAL_RECEIVING = 1, - JD_SERIAL_RECEIVING_HEADER = 2, - JD_SERIAL_TRANSMITTING = 4, - JD_SERIAL_RX_LO_PULSE = 8, - JD_SERIAL_TX_LO_PULSE = 16, - JD_SERIAL_BUS_LO_ERROR = 32, - JD_SERIAL_BUS_TIMEOUT_ERROR = 64, - JD_SERIAL_BUS_UART_ERROR = 128, - JD_SERIAL_ERR_MSK = 224, - JD_SERIAL_BUS_STATE = 256, - JD_SERIAL_BUS_TOGGLED = 512, - JD_SERIAL_DEBUG_BIT = 32768, - JD_SERIAL_EVT_DATA_READY = 1, - JD_SERIAL_EVT_BUS_ERROR = 2, - JD_SERIAL_EVT_CRC_ERROR = 3, - JD_SERIAL_EVT_DRAIN = 4, - JD_SERIAL_EVT_RX_TIMEOUT = 5, - JD_SERIAL_EVT_BUS_CONNECTED = 5, - JD_SERIAL_EVT_BUS_DISCONNECTED = 6, - JD_SERIAL_HEADER_SIZE = 4, - JD_SERIAL_CRC_HEADER_SIZE = 2, - JD_SERIAL_MAX_PAYLOAD_SIZE = 255, - JD_SERIAL_MAXIMUM_BUFFERS = 10, - JD_SERIAL_DMA_TIMEOUT = 2, - JD_SERIAL_MAX_BAUD = 1000000, - JD_SERIAL_TX_MAX_BACKOFF = 1000, - JD_RX_ARRAY_SIZE = 10, - JD_TX_ARRAY_SIZE = 10, - JD_SERIAL_BAUD_1M = 1, - JD_SERIAL_BAUD_500K = 2, - JD_SERIAL_BAUD_250K = 4, - JD_SERIAL_BAUD_125K = 8, - Receiving = 0, - Transmitting = 1, - Unknown = 3, - ListeningForPulse = 0, - ErrorRecovery = 1, - Off = 2, - Baud1M = 1, - Baud500K = 2, - Baud250K = 4, - Baud125K = 8, - Continuation = 0, - BusLoError = 32, - BusTimeoutError = 64, - BusUARTError = 128, - // /libraries/codal-core/inc/JACDAC/JDService.h - JD_MAX_HOST_SERVICES = 16, - JD_SERVICE_EVT_CONNECTED = 65520, - JD_SERVICE_EVT_DISCONNECTED = 65521, - JD_SERVICE_EVT_ERROR = 65526, - JD_SERVICE_NUMBER_UNINITIALISED_VAL = 65535, - JD_SERVICE_STATUS_FLAGS_INITIALISED = 2, - JD_SERVICE_INFO_HEADER_SIZE = 6, - JD_SERVICE_MODE_CLIENT = 1, - JD_SERVICE_MODE_HOST = 2, - JD_SERVICE_MODE_BROADCAST_HOST = 3, - JD_SERVICE_MODE_CONTROL_LAYER = 4, - ClientService = 1, - HostService = 2, - BroadcastHostService = 3, - ControlLayerService = 4, - // /libraries/codal-core/inc/JACDAC/JDServiceClasses.h - STATIC_CLASS_START = 0, - STATIC_CLASS_END = 16777215, - DYNAMIC_CLASS_END = 4294967295, - JD_SERVICE_CLASS_CODAL_START = 0, - JD_SERVICE_CLASS_CODAL_END = 2000, - JD_SERVICE_CLASS_MAKECODE_START = 2000, - JD_SERVICE_CLASS_MAKECODE_END = 4000, - JD_SERVICE_CLASS_CONTROL = 0, - JD_SERVICE_CLASS_CONTROL_RNG = 1, - JD_SERVICE_CLASS_CONTROL_CONFIGURATION = 2, - JD_SERVICE_CLASS_CONTROL_TEST = 3, - JD_SERVICE_CLASS_JOYSTICK = 4, - JD_SERVICE_CLASS_MESSAGE_BUS = 5, - JD_SERVICE_CLASS_BRIDGE = 6, - JD_SERVICE_CLASS_BUTTON = 7, - JD_SERVICE_CLASS_ACCELEROMETER = 8, - JD_SERVICE_CLASS_CONSOLE = 9, - // /libraries/codal-core/inc/JACDAC/control/JDCRC.h - JD_CRC_POLYNOMIAL = 3859, - // /libraries/codal-core/inc/JACDAC/control/JDConfigurationService.h - JD_CONTROL_CONFIGURATION_SERVICE_NUMBER = 1, - JD_CONTROL_CONFIGURATION_SERVICE_REQUEST_TYPE_NAME = 1, - JD_CONTROL_CONFIGURATION_SERVICE_REQUEST_TYPE_IDENTIFY = 2, - JD_CONTROL_CONFIGURATION_SERVICE_PACKET_HEADER_SIZE = 2, - JD_CONTROL_CONFIGURATION_EVT_NAME = 1, - JD_CONTROL_CONFIGURATION_EVT_IDENTIFY = 2, - JD_DEFAULT_INDICATION_TIME = 5, - // /libraries/codal-core/inc/JACDAC/control/JDControlService.h - JD_CONTROL_SERVICE_STATUS_ENUMERATE = 2, - JD_CONTROL_SERVICE_STATUS_ENUMERATING = 4, - JD_CONTROL_SERVICE_STATUS_ENUMERATED = 8, - JD_CONTROL_SERVICE_STATUS_BUS_LO = 16, - JD_CONTROL_SERVICE_EVT_CHANGED = 2, - JD_CONTROL_SERVICE_EVT_TIMER_CALLBACK = 3, - JD_CONTROL_PACKET_HEADER_SIZE = 10, - JD_CONTROL_ROLLING_TIMEOUT_VAL = 3, - // /libraries/codal-core/inc/JACDAC/control/JDDeviceManager.h - JD_DEVICE_FLAGS_NACK = 8, - JD_DEVICE_FLAGS_HAS_NAME = 4, - JD_DEVICE_FLAGS_PROPOSING = 2, - JD_DEVICE_FLAGS_REJECT = 1, - JD_DEVICE_MAX_HOST_SERVICES = 16, - JD_DEVICE_DEFAULT_COMMUNICATION_RATE = 1, - // /libraries/codal-core/inc/JACDAC/control/JDRNGService.h - JD_CONTROL_RNG_SERVICE_NUMBER = 2, - JD_CONTROL_RNG_SERVICE_REQUEST_TYPE_REQ = 1, - JD_CONTROL_RNG_SERVICE_REQUEST_TYPE_RESP = 2, - // /libraries/codal-core/inc/JACDAC/services/JDAccelerometerService.h - JD_ACCEL_EVT_SEND_DATA = 1, - // /libraries/codal-core/inc/JACDAC/services/JDConsoleService.h - JD_CONSOLE_LOG_PRIORITY_LOG = 1, - JD_CONSOLE_LOG_PRIORITY_INFO = 2, - JD_CONSOLE_LOG_PRIORITY_DEBUG = 3, - JD_CONSOLE_LOG_PRIORITY_ERROR = 4, - Log = 1, - Info = 2, - Debug = 3, - // /libraries/codal-core/inc/JACDAC/services/JDMessageBusService.h - JD_MESSAGEBUS_TYPE_EVENT = 1, - JD_MESSAGEBUS_TYPE_LISTEN = 2, // /libraries/codal-core/inc/core/CodalComponent.h DEVICE_ID_BUTTON_A = 1, DEVICE_ID_BUTTON_B = 2, @@ -167,6 +35,16 @@ declare const enum DAL { DEVICE_ID_JACDAC_CONTROL_SERVICE = 32, DEVICE_ID_JACDAC_CONFIGURATION_SERVICE = 33, DEVICE_ID_SYSTEM_ADC = 34, + DEVICE_ID_PULSE_IN = 35, + DEVICE_ID_USB = 36, + DEVICE_ID_SPLITTER = 37, + DEVICE_ID_AUDIO_PROCESSOR = 38, + DEVICE_ID_TAP = 39, + DEVICE_ID_POWER_MANAGER = 40, + DEVICE_ID_PARTIAL_FLASHING = 41, + DEVICE_ID_USB_FLASH_MANAGER = 42, + DEVICE_ID_VIRTUAL_SPEAKER_PIN = 43, + DEVICE_ID_LOG = 44, DEVICE_ID_IO_P0 = 100, DEVICE_ID_MESSAGE_BUS_LISTENER = 1021, DEVICE_ID_NOTIFY_ONE = 1022, @@ -176,6 +54,8 @@ declare const enum DAL { DEVICE_ID_BUTTON_LEFT = 2002, DEVICE_ID_BUTTON_RIGHT = 2003, DEVICE_ID_JD_DYNAMIC_ID = 3000, + DEVICE_ID_DYNAMIC_MIN = 64000, + DEVICE_ID_DYNAMIC_MAX = 65000, DEVICE_COMPONENT_RUNNING = 4096, DEVICE_COMPONENT_STATUS_SYSTEM_TICK = 8192, DEVICE_COMPONENT_STATUS_IDLE_TICK = 16384, @@ -184,6 +64,7 @@ declare const enum DAL { // /libraries/codal-core/inc/core/CodalFiber.h DEVICE_SCHEDULER_RUNNING = 1, DEVICE_SCHEDULER_IDLE = 2, + DEVICE_SCHEDULER_DEEPSLEEP = 4, DEVICE_FIBER_FLAG_FOB = 1, DEVICE_FIBER_FLAG_PARENT = 2, DEVICE_FIBER_FLAG_CHILD = 4, @@ -191,6 +72,8 @@ declare const enum DAL { DEVICE_SCHEDULER_EVT_TICK = 1, DEVICE_SCHEDULER_EVT_IDLE = 2, DEVICE_GET_FIBER_LIST_AVAILABLE = 1, + MUTEX = 0, + SEMAPHORE = 1, // /libraries/codal-core/inc/core/CodalListener.h MESSAGE_BUS_LISTENER_PARAMETERISED = 1, MESSAGE_BUS_LISTENER_METHOD = 2, @@ -218,16 +101,19 @@ declare const enum DAL { DEVICE_SPI_ERROR = -1014, DEVICE_INVALID_STATE = -1015, DEVICE_OOM = 20, + DEVICE_RESORUCES_EXHAUSTED = 21, DEVICE_HEAP_ERROR = 30, DEVICE_NULL_DEREFERENCE = 40, - DEVICE_USB_ERROR = 50, + DEVICE_PERIPHERAL_ERROR = 50, DEVICE_JACDAC_ERROR = 60, + DEVICE_CPU_SDK = 70, DEVICE_HARDWARE_CONFIGURATION_ERROR = 90, // /libraries/codal-core/inc/core/NotifyEvents.h DISPLAY_EVT_FREE = 1, CODAL_SERIAL_EVT_TX_EMPTY = 2, BLE_EVT_SERIAL_TX_EMPTY = 3, ARCADE_PLAYER_JOIN_RESULT = 4, + POWER_EVT_CANCEL_DEEPSLEEP = 5, DEVICE_NOTIFY_USER_EVENT_BASE = 1024, // /libraries/codal-core/inc/driver-models/AbstractButton.h DEVICE_BUTTON_EVT_DOWN = 1, @@ -292,6 +178,7 @@ declare const enum DAL { GYROSCOPE_IMU_DATA_VALID = 2, GYROSCOPE_EVT_DATA_UPDATE = 1, // /libraries/codal-core/inc/driver-models/LowLevelTimer.h + CODAL_LOWLEVELTIMER_STATUS_SLEEP_IRQENABLE = 1, TimerModeTimer = 0, TimerModeCounter = 1, TimerModeAlternateFunction = 2, @@ -309,6 +196,8 @@ declare const enum DAL { IO_STATUS_EVENT_PULSE_ON_EDGE = 64, IO_STATUS_INTERRUPT_ON_EDGE = 128, IO_STATUS_ACTIVE_HI = 256, + IO_STATUS_WAKE_ON_ACTIVE = 512, + IO_STATUS_DISCONNECTING = 1024, DEVICE_PIN_MAX_OUTPUT = 1023, DEVICE_PIN_MAX_SERVO_RANGE = 180, DEVICE_PIN_DEFAULT_SERVO_RANGE = 2000, @@ -358,6 +247,7 @@ declare const enum DAL { CODAL_SERIAL_STATUS_RX_BUFF_INIT = 4, CODAL_SERIAL_STATUS_TX_BUFF_INIT = 8, CODAL_SERIAL_STATUS_RXD = 16, + CODAL_SERIAL_STATUS_DEEPSLEEP = 32, ASYNC = 0, SYNC_SPINWAIT = 1, SYNC_SLEEP = 2, @@ -373,6 +263,8 @@ declare const enum DAL { SingleWireDisconnected = 2, // /libraries/codal-core/inc/driver-models/Timer.h CODAL_TIMER_DEFAULT_EVENT_LIST_SIZE = 10, + CODAL_TIMER_EVENT_FLAGS_NONE = 0, + CODAL_TIMER_EVENT_FLAGS_WAKEUP = 1, // /libraries/codal-core/inc/drivers/AnalogSensor.h ANALOG_THRESHOLD_LOW = 1, ANALOG_THRESHOLD_HIGH = 2, @@ -430,8 +322,8 @@ declare const enum DAL { KEYMAP_KEY_DOWN_Val = 1, KEYMAP_KEY_DOWN_POS = 31, // /libraries/codal-core/inc/drivers/KeyValueStorage.h - DEVICE_KEY_VALUE_STORE_OFFSET = 4, - KEY_VALUE_STORAGE_MAGIC = 49370, + DEVICE_KEY_VALUE_STORE_OFFSET = -4, + KEY_VALUE_STORAGE_MAGIC = 789921, KEY_VALUE_STORAGE_BLOCK_SIZE = 48, KEY_VALUE_STORAGE_KEY_SIZE = 16, KEY_VALUE_STORAGE_SCRATCH_WORD_SIZE = 64, @@ -464,6 +356,8 @@ declare const enum DAL { MULTI_BUTTON_SUPRESSED_1 = 16, MULTI_BUTTON_SUPRESSED_2 = 32, MULTI_BUTTON_ATTACHED = 64, + // /libraries/codal-core/inc/drivers/PulseIn.h + DEVICE_EVT_PULSE_IN_TIMEOUT = 10000, // /libraries/codal-core/inc/drivers/ST7735.h MADCTL_MY = 128, MADCTL_MX = 64, @@ -478,13 +372,12 @@ declare const enum DAL { TOUCH_BUTTON_SENSITIVITY = 10, TOUCH_BUTTON_CALIBRATION_PERIOD = 10, TOUCH_BUTTON_CALIBRATING = 16, + TOUCH_BUTTON_RUNNING = 32, // /libraries/codal-core/inc/drivers/TouchSensor.h TOUCH_SENSOR_MAX_BUTTONS = 10, TOUCH_SENSOR_SAMPLE_PERIOD = 50, TOUCH_SENSE_SAMPLE_MAX = 1000, TOUCH_SENSOR_UPDATE_NEEDED = 1, - // /libraries/codal-core/inc/drivers/USBJACDAC.h - JACDAC_USB_STATUS_CLEAR_TO_SEND = 2, // /libraries/codal-core/inc/drivers/USB_HID_Keys.h KEY_MOD_LCTRL = 1, KEY_MOD_LSHIFT = 2, @@ -679,6 +572,8 @@ declare const enum DAL { DATASTREAM_FORMAT_24BIT_SIGNED = 6, DATASTREAM_FORMAT_32BIT_UNSIGNED = 7, DATASTREAM_FORMAT_32BIT_SIGNED = 8, + // /libraries/codal-core/inc/streams/FIFOStream.h + FIFO_MAXIMUM_BUFFERS = 256, // /libraries/codal-core/inc/streams/LevelDetector.h LEVEL_THRESHOLD_LOW = 1, LEVEL_THRESHOLD_HIGH = 2, @@ -690,9 +585,36 @@ declare const enum DAL { LEVEL_DETECTOR_SPL_INITIALISED = 1, LEVEL_DETECTOR_SPL_HIGH_THRESHOLD_PASSED = 2, LEVEL_DETECTOR_SPL_LOW_THRESHOLD_PASSED = 4, + LEVEL_DETECTOR_SPL_CLAP = 8, LEVEL_DETECTOR_SPL_DEFAULT_WINDOW_SIZE = 128, + LEVEL_DETECTOR_SPL_NORMALIZE = 1, + LEVEL_DETECTOR_SPL_MIN_BUFFERS = 2, + LEVEL_DETECTOR_SPL_DB = 1, + LEVEL_DETECTOR_SPL_8BIT = 2, + LEVEL_DETECTOR_SPL_BEGIN_POSS_CLAP_RMS = 200, + LEVEL_DETECTOR_SPL_MIN_IN_CLAP_RMS = 300, + LEVEL_DETECTOR_SPL_CLAP_OVER_RMS = 100, + LEVEL_DETECTOR_SPL_CLAP_MAX_LOUD_BLOCKS = 13, + LEVEL_DETECTOR_SPL_CLAP_MIN_LOUD_BLOCKS = 2, + LEVEL_DETECTOR_SPL_CLAP_MIN_QUIET_BLOCKS = 20, // /libraries/codal-core/inc/streams/MemorySource.h MEMORY_SOURCE_DEFAULT_MAX_BUFFER = 256, + // /libraries/codal-core/inc/streams/StreamFlowTrigger.h + TRIGGER_PULL = 1, + TRIGGER_REQUEST = 2, + // /libraries/codal-core/inc/streams/StreamRecording.h + CODAL_DEFAULT_STREAM_RECORDING_MAX_LENGTH = 50000, + REC_STATE_STOPPED = 0, + REC_STATE_PLAYING = 1, + REC_STATE_RECORDING = 2, + // /libraries/codal-core/inc/streams/StreamSplitter.h + CONFIG_MAX_CHANNELS = 10, + CONFIG_SPLITTER_OVERSAMPLE_STEP = 16, + SPLITTER_CHANNEL_CONNECT = 1, + SPLITTER_CHANNEL_DISCONNECT = 2, + SPLITTER_ACTIVATE = 3, + SPLITTER_DEACTIVATE = 4, + SPLITTER_TICK = 10, // /libraries/codal-core/inc/streams/Synthesizer.h SYNTHESIZER_SAMPLE_RATE = 44100, TONE_WIDTH = 1024, @@ -717,6 +639,14 @@ declare const enum DAL { CREATE_ONLY = 0, CREATE_AND_FIRE = 1, DEVICE_EVENT_DEFAULT_LAUNCH_MODE = 1, + // /libraries/codal-core/inc/types/ManagedBuffer.h + Zero = 1, + // /libraries/codal-microbit-v2/inc/FSCache.h + FSCACHE_FLAG_PINNED = 1, + CODAL_FS_CACHE_VALIDATE = 1, + CODAL_FS_DEFAULT_CACHE_SZE = 4, + // /libraries/codal-microbit-v2/inc/MicroBitAudio.h + MICROBIT_AUDIO_STATUS_DEEPSLEEP = 1, // /libraries/codal-microbit-v2/inc/MicroBitBLEServices.h MICROBIT_BLE_SERVICES_MAX = 20, MICROBIT_BLE_SERVICES_OBSERVER_PRIO = 2, @@ -749,11 +679,34 @@ declare const enum DAL { MBFS_BLOCK_TYPE_FILE = 1, MBFS_BLOCK_TYPE_DIRECTORY = 2, MBFS_BLOCK_TYPE_FILETABLE = 3, + // /libraries/codal-microbit-v2/inc/MicroBitLog.h + CONFIG_MICROBIT_LOG_METADATA_SIZE = 2048, + CONFIG_MICROBIT_LOG_JOURNAL_SIZE = 4096, + CONFIG_MICROBIT_LOG_CACHE_BLOCK_SIZE = 256, + MICROBIT_LOG_JOURNAL_ENTRY_SIZE = 8, + MICROBIT_LOG_STATUS_INITIALIZED = 1, + MICROBIT_LOG_STATUS_ROW_STARTED = 2, + MICROBIT_LOG_STATUS_FULL = 4, + MICROBIT_LOG_STATUS_SERIAL_MIRROR = 8, + MICROBIT_LOG_EVT_LOG_FULL = 1, + Milliseconds = 1, + Seconds = 10, + Minutes = 600, + Hours = 36000, + Days = 864000, + HTMLHeader = 0, + HTML = 1, + CSV = 2, // /libraries/codal-microbit-v2/inc/MicroBitMemoryMap.h NUMBER_OF_REGIONS = 3, + REGION_SD = 0, + REGION_CODAL = 1, + REGION_MAKECODE = 2, + REGION_PYTHON = 3, // /libraries/codal-microbit-v2/inc/MicroBitPowerManager.h - MICROBIT_UIPM_MAX_BUFFER_SIZE = 8, - MICROBIT_UIPM_MAX_RETRIES = 5, + MICROBIT_UIPM_MAX_BUFFER_SIZE = 12, + MICROBIT_UIPM_MAX_RETRIES = 20, + MICROBIT_USB_INTERFACE_IRQ_THRESHOLD = 30, MICROBIT_UIPM_COMMAND_READ_REQUEST = 16, MICROBIT_UIPM_COMMAND_READ_RESPONSE = 17, MICROBIT_UIPM_COMMAND_WRITE_REQUEST = 18, @@ -780,33 +733,36 @@ declare const enum DAL { MICROBIT_UIPM_READ_FORBIDDEN = 54, MICROBIT_UIPM_WRITE_FORBIDDEN = 55, MICROBIT_UIPM_WRITE_FAIL = 56, - MICROBIT_UIPM_I2C_FAIL = 57, + MICROBIT_UIPM_BUSY = 57, MICROBIT_USB_INTERFACE_POWER_MODE_VLPS = 6, MICROBIT_USB_INTERFACE_POWER_MODE_VLLS0 = 8, MICROBIT_USB_INTERFACE_AWAITING_RESPONSE = 1, MICROBIT_USB_INTERFACE_VERSION_LOADED = 2, + MICROBIT_USB_INTERFACE_ALWAYS_NOP = 4, + MICROBIT_USB_INTERFACE_BUSY_FLAG_SUPPORTED = 32, + CONFIG_MINIMUM_DEEP_SLEEP_TIME = 100, + CONFIG_MINIMUM_POWER_ON_TIME = 500, // /libraries/codal-microbit-v2/inc/MicroBitRadio.h MICROBIT_RADIO_STATUS_INITIALISED = 1, + MICROBIT_RADIO_STATUS_DEEPSLEEP_IRQ = 2, + MICROBIT_RADIO_STATUS_DEEPSLEEP_INIT = 4, MICROBIT_RADIO_BASE_ADDRESS = 1969383796, MICROBIT_RADIO_DEFAULT_GROUP = 0, - MICROBIT_RADIO_DEFAULT_TX_POWER = 7, + MICROBIT_RADIO_DEFAULT_TX_POWER = 6, MICROBIT_RADIO_DEFAULT_FREQUENCY = 7, - MICROBIT_RADIO_MAX_PACKET_SIZE = 32, MICROBIT_RADIO_HEADER_SIZE = 4, MICROBIT_RADIO_MAXIMUM_RX_BUFFERS = 4, - MICROBIT_RADIO_POWER_LEVELS = 10, + MICROBIT_RADIO_POWER_LEVELS = 8, MICROBIT_RADIO_PROTOCOL_DATAGRAM = 1, MICROBIT_RADIO_PROTOCOL_EVENTBUS = 2, MICROBIT_RADIO_EVT_DATAGRAM = 1, - // /libraries/codal-microbit-v2/inc/MicroBitStorage.h - MICROBIT_STORAGE_MAGIC = 51966, - MICROBIT_STORAGE_BLOCK_SIZE = 48, - MICROBIT_STORAGE_KEY_SIZE = 16, // /libraries/codal-microbit-v2/inc/MicroBitThermometer.h MICROBIT_THERMOMETER_PERIOD = 1000, MICROBIT_THERMOMETER_EVT_UPDATE = 1, // /libraries/codal-microbit-v2/inc/MicroBitUSBFlashManager.h - MICROBIT_USB_FLASH_MAX_RETRIES = 4, + MICROBIT_USB_FLASH_MAX_TX_RETRIES = 20, + MICROBIT_USB_FLASH_MAX_RX_RETRIES = 20, + MICROBIT_USB_FLASH_MAX_FLASH_STORAGE = 126976, MICROBIT_USB_FLASH_FILENAME_CMD = 1, MICROBIT_USB_FLASH_FILESIZE_CMD = 2, MICROBIT_USB_FLASH_VISIBILITY_CMD = 3, @@ -822,10 +778,25 @@ declare const enum DAL { MICROBIT_USB_FLASH_AWAITING_RESPONSE = 1, MICROBIT_USB_FLASH_GEOMETRY_LOADED = 2, MICROBIT_USB_FLASH_CONFIG_LOADED = 4, + MICROBIT_USB_FLASH_SINGLE_PAGE_ERASE_ONLY = 8, + MICROBIT_USB_FLASH_USE_NULL_TRANSACTION = 16, + MICROBIT_USB_FLASH_BUSY_FLAG_SUPPORTED = 32, + MICROBIT_USB_FLASH_100MS_AFTER_ERASE = 64, + // /libraries/codal-microbit-v2/inc/MicroSynth.h + Saw = 0, + Pulse = 1, + Triangle = 2, + LPF = 0, + HPF = 1, + BPF = 2, // /libraries/codal-microbit-v2/inc/Mixer2.h CONFIG_MIXER_BUFFER_SIZE = 512, CONFIG_MIXER_INTERNAL_RANGE = 1023, CONFIG_MIXER_DEFAULT_SAMPLERATE = 44100, + CONFIG_MIXER_DEFAULT_CHANNEL_SAMPLERATE = 44100, + DEVICE_ID_MIXER = 3030, + DEVICE_MIXER_EVT_SILENCE = 1, + DEVICE_MIXER_EVT_SOUND = 2, // /libraries/codal-microbit-v2/inc/NRF52LedMatrix.h NRF52_LED_MATRIX_CLOCK_FREQUENCY = 16000000, NRF52_LED_MATRIX_FREQUENCY = 60, @@ -834,13 +805,17 @@ declare const enum DAL { NRF52_LEDMATRIX_GPIOTE_CHANNEL_BASE = 1, NRF52_LEDMATRIX_PPI_CHANNEL_BASE = 3, NRF52_LEDMATRIX_STATUS_RESET = 1, + NRF52_LEDMATRIX_STATUS_LIGHTREADY = 2, // /libraries/codal-microbit-v2/inc/SoundEmojiSynthesizer.h + CONFIG_EMOJI_SYNTHESIZER_OUTPUT_BUFFER_DEPTH = 3, EMOJI_SYNTHESIZER_SAMPLE_RATE = 44100, EMOJI_SYNTHESIZER_TONE_WIDTH = 1024, EMOJI_SYNTHESIZER_BUFFER_SIZE = 512, EMOJI_SYNTHESIZER_TONE_EFFECT_PARAMETERS = 2, EMOJI_SYNTHESIZER_TONE_EFFECTS = 3, EMOJI_SYNTHESIZER_STATUS_ACTIVE = 1, + EMOJI_SYNTHESIZER_STATUS_OUTPUT_SILENCE_AS_EMPTY = 2, + EMOJI_SYNTHESIZER_STATUS_STOPPING = 4, DEVICE_ID_SOUND_EMOJI_SYNTHESIZER_0 = 3010, DEVICE_ID_SOUND_EMOJI_SYNTHESIZER_1 = 3011, DEVICE_ID_SOUND_EMOJI_SYNTHESIZER_2 = 3012, @@ -852,8 +827,22 @@ declare const enum DAL { DEVICE_ID_SOUND_EMOJI_SYNTHESIZER_8 = 3018, DEVICE_ID_SOUND_EMOJI_SYNTHESIZER_9 = 3019, DEVICE_SOUND_EMOJI_SYNTHESIZER_EVT_DONE = 1, + DEVICE_SOUND_EMOJI_SYNTHESIZER_EVT_PLAYBACK_COMPLETE = 2, + SFX_DEFAULT_VIBRATO_STEPS = 512, + SFX_DEFAULT_VIBRATO_PARAM = 2, + SFX_DEFAULT_TREMOLO_STEPS = 900, + SFX_DEFAULT_TREMOLO_PARAM = 3, + SFX_DEFAULT_WARBLE_STEPS = 700, + SFX_DEFAULT_WARBLE_PARAM = 2, // /libraries/codal-microbit-v2/inc/SoundOutputPin.h - CONFIG_SOUND_OUTPUT_PIN_PERIOD = 50, + CONFIG_SOUND_OUTPUT_PIN_PERIOD = 5, + CONFIG_SOUND_OUTPUT_PIN_SILENCE_GATE = 100, + SOUND_OUTPUT_PIN_SAMPLE_RATE = 44100, + SOUND_OUTPUT_PIN_BUFFER_SIZE = 512, + CONFIG_SOUND_OUTPUT_PIN_DISCRETE_OUTPUT = 1, + SOUND_OUTPUT_PIN_STATUS_ENABLED = 1, + SOUND_OUTPUT_PIN_STATUS_ACTIVE = 2, + CONFIG_SOUND_OUTPUT_PIN_TONEPRINT = 0, // /libraries/codal-microbit-v2/inc/bluetooth/ExternalEvents.h MICROBIT_ID_BLE = 1000, MICROBIT_ID_BLE_UART = 1200, @@ -931,7 +920,6 @@ declare const enum DAL { MICROBIT_BLE_PAIR_SUCCESSFUL = 8, MICROBIT_BLE_PAIRING_TIMEOUT = 90, MICROBIT_BLE_POWER_LEVELS = 8, - MICROBIT_BLE_MAXIMUM_BONDS = 4, MICROBIT_BLE_EDDYSTONE_ADV_INTERVAL = 400, MICROBIT_BLE_EDDYSTONE_DEFAULT_POWER = 240, MICROBIT_BLE_STATUS_DISCONNECT = 4, @@ -1096,10 +1084,12 @@ declare const enum DAL { MICROBIT_ID_RADIO_DATA_READY = 10, MICROBIT_ID_SERIAL = 12, MICROBIT_ID_THERMOMETER = 8, - MICROBIT_ID_PARTIAL_FLASHING = 36, - MICROBIT_ID_POWER_MANAGER = 37, - MICROBIT_ID_USB_FLASH_MANAGER = 38, - MICROBIT_ID_VIRTUAL_SPEAKER_PIN = 39, + MICROBIT_ID_POWER_MANAGER = 40, + MICROBIT_ID_PARTIAL_FLASHING = 41, + MICROBIT_ID_USB_FLASH_MANAGER = 42, + MICROBIT_ID_VIRTUAL_SPEAKER_PIN = 43, + MICROBIT_ID_LOG = 44, + MICROBIT_ID_UTILITY = 45, MICROBIT_NESTED_HEAP_SIZE = 0, MICROBIT_SCHEDULER_RUNNING = 1, MICROBIT_SERIAL_DEFAULT_BAUD_RATE = 115200, @@ -1108,7 +1098,7 @@ declare const enum DAL { MICROBIT_COMPASS_STATUS_ADDED_TO_IDLE = 8, // /libraries/codal-microbit-v2/model/MicroBit.h DEVICE_INITIALIZED = 1, - MICROBIT_UBIT_FACE_TOUCH_BUTTON = 1, + KL27_POWER_ON_DELAY = 1000, DEVICE_ID_MICROPHONE = 3001, // /libraries/codal-microbit-v2/model/MicroBitIO.h MICROBIT_PIN_BUTTON_RESET = -1, @@ -1165,6 +1155,7 @@ declare const enum DAL { IO_SAVED_STATUS_OUTPUT_HI = 2, IO_SAVED_STATUS_DETECT_LOW_ENABLED = 4, IO_SAVED_STATUS_DETECT_HIGH_ENABLED = 8, + IO_SAVED_STATUS_SAVED = 1, // /pxtapp/configkeys.h CFG_PIN_NAME_MSK = 65535, CFG_PIN_CONFIG_MSK = 4294901760, @@ -1263,6 +1254,9 @@ declare const enum DAL { CFG_PIN_WIFI_AT_TX = 91, CFG_PIN_WIFI_AT_RX = 92, CFG_PIN_USB_POWER = 93, + CFG_DISPLAY_DELAY = 94, + CFG_SETTINGS_SIZE_DEFL = 95, + CFG_SETTINGS_SIZE = 96, ACCELEROMETER_TYPE_LIS3DH = 50, ACCELEROMETER_TYPE_LIS3DH_ALT = 48, ACCELEROMETER_TYPE_MMA8453 = 56, @@ -1476,6 +1470,38 @@ declare const enum DAL { CFG_PIN_P29 = 429, CFG_PIN_P30 = 430, CFG_PIN_P31 = 431, + CFG_PIN_P32 = 432, + CFG_PIN_P33 = 433, + CFG_PIN_P34 = 434, + CFG_PIN_P35 = 435, + CFG_PIN_P36 = 436, + CFG_PIN_P37 = 437, + CFG_PIN_P38 = 438, + CFG_PIN_P39 = 439, + CFG_PIN_P40 = 440, + CFG_PIN_P41 = 441, + CFG_PIN_P42 = 442, + CFG_PIN_P43 = 443, + CFG_PIN_P44 = 444, + CFG_PIN_P45 = 445, + CFG_PIN_P46 = 446, + CFG_PIN_P47 = 447, + CFG_PIN_P48 = 448, + CFG_PIN_P49 = 449, + CFG_PIN_P50 = 450, + CFG_PIN_P51 = 451, + CFG_PIN_P52 = 452, + CFG_PIN_P53 = 453, + CFG_PIN_P54 = 454, + CFG_PIN_P55 = 455, + CFG_PIN_P56 = 456, + CFG_PIN_P57 = 457, + CFG_PIN_P58 = 458, + CFG_PIN_P59 = 459, + CFG_PIN_P60 = 460, + CFG_PIN_P61 = 461, + CFG_PIN_P62 = 462, + CFG_PIN_P63 = 463, CFG_PIN_LORA_MISO = 1001, CFG_PIN_LORA_MOSI = 1002, CFG_PIN_LORA_SCK = 1003, @@ -1518,12 +1544,73 @@ declare const enum DAL { CFG_PIN_GROVE0 = 1040, CFG_PIN_GROVE1 = 1041, CFG_PIN_SS = 1042, + CFG_PIN_D33 = 183, + CFG_PIN_D34 = 184, + CFG_PIN_D35 = 185, + CFG_PIN_D36 = 186, + CFG_PIN_D37 = 187, + CFG_PIN_D38 = 188, + CFG_PIN_D39 = 189, + CFG_PIN_D40 = 190, + CFG_PIN_D41 = 191, + CFG_PIN_D42 = 192, + CFG_PIN_D43 = 193, + CFG_PIN_D44 = 194, + CFG_PIN_D45 = 195, + CFG_PIN_D46 = 196, + CFG_PIN_D47 = 197, + CFG_PIN_D48 = 198, + CFG_PIN_D49 = 199, + CFG_PIN_D50 = 259, + CFG_PIN_D51 = 260, + CFG_PIN_D52 = 261, + CFG_PIN_D53 = 262, + CFG_PIN_TX1 = 263, + CFG_PIN_TX2 = 264, + CFG_PIN_TX3 = 265, + CFG_PIN_RX1 = 266, + CFG_PIN_RX2 = 267, + CFG_PIN_RX3 = 268, + CFG_PIN_SCL1 = 269, + CFG_PIN_SDA1 = 270, + CFG_PIN_PCC_D0 = 271, + CFG_PIN_PCC_D1 = 272, + CFG_PIN_PCC_D2 = 273, + CFG_PIN_PCC_D3 = 274, + CFG_PIN_PCC_D4 = 275, + CFG_PIN_PCC_D5 = 276, + CFG_PIN_PCC_D6 = 277, + CFG_PIN_PCC_D7 = 278, + CFG_PIN_PCC_D8 = 279, + CFG_PIN_PCC_D9 = 280, + CFG_PIN_PCC_D10 = 281, + CFG_PIN_PCC_D11 = 282, + CFG_PIN_PCC_D12 = 283, + CFG_PIN_PCC_D13 = 284, + CFG_PIN_CC_DEN1 = 285, + CFG_PIN_CC_DEN2 = 286, + CFG_PIN_CC_CLK = 287, + CFG_PIN_XCC_CLK = 288, CFG_PIN_JDPWR_PRE_SENSE = 1100, CFG_PIN_JDPWR_GND_SENSE = 1101, CFG_PIN_JDPWR_PULSE = 1102, CFG_PIN_JDPWR_OVERLOAD_LED = 1103, CFG_PIN_JDPWR_ENABLE = 1104, CFG_PIN_JDPWR_FAULT = 1105, + CFG_USER_CFG_0 = 2000, + CFG_USER_CFG_1 = 2001, + CFG_USER_CFG_2 = 2002, + CFG_USER_CFG_3 = 2003, + CFG_USER_CFG_4 = 2004, + CFG_USER_CFG_5 = 2005, + CFG_USER_CFG_6 = 2006, + CFG_USER_CFG_7 = 2007, + CFG_USER_CFG_8 = 2008, + CFG_USER_CFG_9 = 2009, + CFG_ARCADE_CFG_0 = 2100, + CFG_ARCADE_CFG_1 = 2101, + CFG_ARCADE_SCREEN_WIDTH = 2102, + CFG_ARCADE_SCREEN_HEIGHT = 2103, // /pxtapp/platform.h PXT_MICROBIT_TAGGED_INT = 1, PXT_POWI = 1, @@ -1533,6 +1620,7 @@ declare const enum DAL { PXT64 = 1, PXT_REFCNT_FLASH = 65534, VTABLE_MAGIC = 249, + VTABLE_MAGIC2 = 248, Undefined = 0, Boolean = 1, Number = 2, @@ -1549,8 +1637,10 @@ declare const enum DAL { RefMap = 8, RefMImage = 9, MMap = 10, + BoxedString_SkipList = 11, + BoxedString_ASCII = 12, + ZPin = 13, User0 = 16, - PXT_IOS_HEAP_ALLOC_BITS = 20, IMAGE_HEADER_MAGIC = 135, Int8LE = 1, UInt8LE = 2, @@ -1570,8 +1660,10 @@ declare const enum DAL { Float64BE = 16, NUM_TRY_FRAME_REGS = 3, GC = 0, + PERF_NOW_MASK = 4294967295, + PERF_NOW_SCALE = 1, + PXT_STRING_SKIP_INCR = 16, // /pxtapp/pxtcore.h - GC_MAX_ALLOC_SIZE = 9000, NON_GC_HEAP_RESERVATION = 1024, GC_BLOCK_SIZE = 256, } diff --git a/libs/core/enums.d.ts b/libs/core/enums.d.ts index 9086e011805..b1cee579e99 100644 --- a/libs/core/enums.d.ts +++ b/libs/core/enums.d.ts @@ -428,63 +428,88 @@ declare namespace led { declare const enum DigitalPin { + //% blockIdentity="pins._digitalPin" P0 = 100, // MICROBIT_ID_IO_P0 + //% blockIdentity="pins._digitalPin" P1 = 101, // MICROBIT_ID_IO_P1 + //% blockIdentity="pins._digitalPin" P2 = 102, // MICROBIT_ID_IO_P2 + //% blockIdentity="pins._digitalPin" P3 = 103, // MICROBIT_ID_IO_P3 + //% blockIdentity="pins._digitalPin" P4 = 104, // MICROBIT_ID_IO_P4 + //% blockIdentity="pins._digitalPin" P5 = 105, // MICROBIT_ID_IO_P5 + //% blockIdentity="pins._digitalPin" P6 = 106, // MICROBIT_ID_IO_P6 + //% blockIdentity="pins._digitalPin" P7 = 107, // MICROBIT_ID_IO_P7 + //% blockIdentity="pins._digitalPin" P8 = 108, // MICROBIT_ID_IO_P8 + //% blockIdentity="pins._digitalPin" P9 = 109, // MICROBIT_ID_IO_P9 + //% blockIdentity="pins._digitalPin" P10 = 110, // MICROBIT_ID_IO_P10 + //% blockIdentity="pins._digitalPin" P11 = 111, // MICROBIT_ID_IO_P11 + //% blockIdentity="pins._digitalPin" P12 = 112, // MICROBIT_ID_IO_P12 + //% blockIdentity="pins._digitalPin" P13 = 113, // MICROBIT_ID_IO_P13 + //% blockIdentity="pins._digitalPin" P14 = 114, // MICROBIT_ID_IO_P14 + //% blockIdentity="pins._digitalPin" P15 = 115, // MICROBIT_ID_IO_P15 + //% blockIdentity="pins._digitalPin" P16 = 116, // MICROBIT_ID_IO_P16 + //% blockIdentity="pins._digitalPin" //% blockHidden=1 P19 = 119, // MICROBIT_ID_IO_P19 + //% blockIdentity="pins._digitalPin" //% blockHidden=1 P20 = 120, // MICROBIT_ID_IO_P20 } declare const enum AnalogPin { + //% blockIdentity="pins._analogPin" P0 = 100, // MICROBIT_ID_IO_P0 + //% blockIdentity="pins._analogPin" P1 = 101, // MICROBIT_ID_IO_P1 + //% blockIdentity="pins._analogPin" P2 = 102, // MICROBIT_ID_IO_P2 + //% blockIdentity="pins._analogPin" P3 = 103, // MICROBIT_ID_IO_P3 + //% blockIdentity="pins._analogPin" P4 = 104, // MICROBIT_ID_IO_P4 - P10 = 110, // MICROBIT_ID_IO_P10 - //% block="P5 (write only)" + //% blockIdentity="pins._analogPin" P5 = 105, // MICROBIT_ID_IO_P5 - //% block="P6 (write only)" + //% blockIdentity="pins._analogPin" P6 = 106, // MICROBIT_ID_IO_P6 - //% block="P7 (write only)" + //% blockIdentity="pins._analogPin" P7 = 107, // MICROBIT_ID_IO_P7 - //% block="P8 (write only)" + //% blockIdentity="pins._analogPin" P8 = 108, // MICROBIT_ID_IO_P8 - //% block="P9 (write only)" + //% blockIdentity="pins._analogPin" P9 = 109, // MICROBIT_ID_IO_P9 - //% block="P11 (write only)" + //% blockIdentity="pins._analogPin" + P10 = 110, // MICROBIT_ID_IO_P10 + //% blockIdentity="pins._analogPin" P11 = 111, // MICROBIT_ID_IO_P11 - //% block="P12 (write only)" + //% blockIdentity="pins._analogPin" P12 = 112, // MICROBIT_ID_IO_P12 - //% block="P13 (write only)" + //% blockIdentity="pins._analogPin" P13 = 113, // MICROBIT_ID_IO_P13 - //% block="P14 (write only)" + //% blockIdentity="pins._analogPin" P14 = 114, // MICROBIT_ID_IO_P14 - //% block="P15 (write only)" + //% blockIdentity="pins._analogPin" P15 = 115, // MICROBIT_ID_IO_P15 - //% block="P16 (write only)" + //% blockIdentity="pins._analogPin" P16 = 116, // MICROBIT_ID_IO_P16 - //% block="P19 (write only)" + //% blockIdentity="pins._analogPin" //% blockHidden=1 P19 = 119, // MICROBIT_ID_IO_P19 - //% block="P20 (write only)" + //% blockIdentity="pins._analogPin" //% blockHidden=1 P20 = 120, // MICROBIT_ID_IO_P20 } diff --git a/libs/core/game.ts b/libs/core/game.ts index 103741b4cdf..932135d1d09 100644 --- a/libs/core/game.ts +++ b/libs/core/game.ts @@ -44,6 +44,7 @@ namespace game { */ //% weight=60 blockGap=8 help=game/create-sprite //% blockId=game_create_sprite block="create sprite at|x: %x|y: %y" + //% x.label="x" y.label="y" //% parts="ledmatrix" export function createSprite(x: number, y: number): LedSprite { init(); @@ -66,6 +67,7 @@ namespace game { */ //% weight=10 help=game/add-score //% blockId=game_add_score block="change score by|%points" blockGap=8 + //% points.label="value" //% parts="ledmatrix" export function addScore(points: number): void { setScore(_score + points); @@ -89,6 +91,7 @@ namespace game { */ //% weight=9 help=game/start-countdown //% blockId=game_start_countdown block="start countdown|(ms) %duration" blockGap=8 + //% ms.label="value" //% parts="ledmatrix" export function startCountdown(ms: number): void { if (checkStart()) { @@ -153,6 +156,7 @@ namespace game { * @param value new score value. */ //% blockId=game_set_score block="set score %points" blockGap=8 + //% value.label="value" //% weight=10 help=game/set-score export function setScore(value: number): void { _score = Math.max(0, value); @@ -172,6 +176,7 @@ namespace game { */ //% weight=10 help=game/set-life //% blockId=game_set_life block="set life %value" blockGap=8 + //% value.label="value" export function setLife(value: number): void { _life = Math.max(0, value); if (_life <= 0) { @@ -185,6 +190,7 @@ namespace game { */ //% weight=10 help=game/add-life //% blockId=game_add_life block="add life %lives" blockGap=8 + //% lives.label="value" export function addLife(lives: number): void { setLife(_life + lives); } @@ -208,6 +214,7 @@ namespace game { //% weight=10 help=game/remove-life //% parts="ledmatrix" //% blockId=game_remove_life block="remove life %life" blockGap=8 + //% life.label="value" export function removeLife(life: number): void { setLife(_life - life); if (!_paused && !_backgroundAnimation) { @@ -305,7 +312,7 @@ namespace game { * Resumes the game rendering engine */ //% blockId=game_resume block="resume" - //% advanced=true blockGap=8 help=game/resumeP + //% advanced=true blockGap=8 help=game/resume export function resume(): void { _paused = false; plot(); @@ -360,6 +367,7 @@ namespace game { */ //% weight=50 help=game/move //% blockId=game_move_sprite block="%sprite|move by %leds" blockGap=8 + //% sprite.label="sprite" leds.label="LEDs" //% parts="ledmatrix" public move(leds: number): void { if (this._dir == 0) { @@ -409,6 +417,7 @@ namespace game { */ //% weight=18 help=game/if-on-edge-bounce //% blockId=game_sprite_bounce block="%sprite|if on edge, bounce" + //% sprite.label="sprite" //% parts="ledmatrix" public ifOnEdgeBounce(): void { if (this._dir == 0 && this._y == 0) { @@ -463,6 +472,7 @@ namespace game { */ //% weight=49 help=game/turn //% blockId=game_turn_sprite block="%sprite|turn %direction|by (°) %degrees" + //% sprite.label="sprite" degrees.label="degrees" public turn(direction: Direction, degrees: number) { if (direction == Direction.Right) this.setDirection(this._dir + degrees); @@ -495,6 +505,7 @@ namespace game { */ //% weight=29 help=game/set //% blockId=game_sprite_set_property block="%sprite|set %property|to %value" blockGap=8 + //% sprite.label="sprite" value.label="value" public set(property: LedSpriteProperty, value: number) { switch (property) { case LedSpriteProperty.X: this.setX(value); break; @@ -512,6 +523,7 @@ namespace game { */ //% weight=30 help=game/change //% blockId=game_sprite_change_xy block="%sprite|change %property|by %value" blockGap=8 + //% sprite.label="sprite" value.label="value" public change(property: LedSpriteProperty, value: number) { switch (property) { case LedSpriteProperty.X: this.changeXBy(value); break; @@ -528,6 +540,7 @@ namespace game { */ //% weight=28 help=game/get //% blockId=game_sprite_property block="%sprite|%property" + //% sprite.label="sprite" public get(property: LedSpriteProperty) { switch (property) { case LedSpriteProperty.X: return this.x(); @@ -622,6 +635,7 @@ namespace game { */ //% weight=20 help=game/is-touching //% blockId=game_sprite_touching_sprite block="is %sprite|touching %other" blockGap=8 + //% sprite.label="sprite" other.label="other sprite" public isTouching(other: LedSprite): boolean { return this._enabled && other._enabled && this._x == other._x && this._y == other._y; } @@ -632,6 +646,7 @@ namespace game { */ //% weight=19 help=game/is-touching-edge //% blockId=game_sprite_touching_edge block="is %sprite|touching edge" blockGap=8 + //% sprite.label="sprite" public isTouchingEdge(): boolean { return this._enabled && (this._x == 0 || this._x == 4 || this._y == 0 || this._y == 4); } @@ -697,6 +712,7 @@ namespace game { */ //% weight=59 blockGap=8 help=game/delete //% blockId="game_delete_sprite" block="delete %this(sprite)" + //% this.label="sprite" public delete(): void { this._enabled = false; if (_sprites.removeElement(this)) @@ -708,6 +724,7 @@ namespace game { */ //% weight=58 help=game/is-deleted //% blockId="game_sprite_is_deleted" block="is %sprite|deleted" + //% sprite.label="sprite" public isDeleted(): boolean { return !this._enabled; } diff --git a/libs/core/helpers.ts b/libs/core/helpers.ts index 6caa902e620..b5ed80f980f 100644 --- a/libs/core/helpers.ts +++ b/libs/core/helpers.ts @@ -1,10 +1,41 @@ +enum UnitConversion { + //% block="degrees to radians" + DegreesToRadians, + //% block="radians to degrees" + RadiansToDegrees, + //% block="celsius to fahrenheit" + CelsiusToFahrenheit, + //% block="fahrenheit to celsius" + FahrenheitToCelsius +} + namespace Math { /** * Generates a `true` or `false` value randomly, just like flipping a coin. */ //% blockId=logic_random block="pick random true or false" - //% help=math/random-boolean weight=0 + //% help=math/random-boolean weight=1 export function randomBoolean(): boolean { return Math.randomRange(0, 1) === 1; } + + /** + * Converts a value from one unit to another. For example, degrees to radians, fahrenheit to celsius, etc. + * @param value The value to convert. + * @param type The type of conversion to perform. + */ + //% blockId=math_convert_unit + //% block="convert $value|from $type" + //% value.label="value" + //% help=math/convert + //% weight=0 + export function convert(value: number, type: UnitConversion): number { + switch (type) { + case UnitConversion.DegreesToRadians: return value * 0.017453292519943295; + case UnitConversion.RadiansToDegrees: return value * 57.29577951308232; + case UnitConversion.CelsiusToFahrenheit: return (value * 1.8) + 32; + case UnitConversion.FahrenheitToCelsius: return (value - 32) * 0.5555555555555556; + } + return value; + } } \ No newline at end of file diff --git a/libs/core/icons.jres b/libs/core/icons.jres index 3e4e037dff1..49bc248145c 100644 --- a/libs/core/icons.jres +++ b/libs/core/icons.jres @@ -90,7 +90,7 @@ "quarternote": { "icon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAE8ElEQVR4nO3dQY4VZRQF4PtMnBDmoAtwAyzAuCeYEKY6wT0ZnbMBFwAyJ0wYPAfdxnTaOl3V/1/P+4rvm1pwT97LAUk6daoAAACAFk4P/PenVfWsqp5U1beTb3+pqs9V9bGqPj3y92id73w+/1hVr6rqRVU9nxetqqr+qqp3VfX2dDr9/sjfo/XnVw3ypYI8r6rvJ4da8r5uvvAtWuc7n8+vq+rnfeLcPVVVb06n0y8bf13rz6+a5FsqyNOq+mG3OP/tz1r/J03rfLd/c/xWD/8NPcu5qn7a8DdJ68+vGuX7ZuHhZ/tmGb7ZPd+rulw56vbWyw3Pd//82uRbKsiTHYMs2XKze74Xu6WYc7P759cm31JBZv+DaI0tN7vnm/0P8jW+2/Bs98+vTb6lggClIBApCAQKAoGCQKAgECgIBAoCgYJAoCAQKAgECgKBgkCgIBAoCAQKAoGCQKAgECgIBEsF+XLRFNtvds+39R1QM3zY8Gz3z69NvqWCfN4xyJItN7vne7dbijk3u39+bfItFeTjjkGWbLnZPd/bunmZ26Wcb2+u1f3za5NvqSCf6uZ1jJfyvra9v7V1vts3HL6py5Tkn1eP/rHh17T+/KpRPi+vXjbr5dUv6+alblveW7XGh7r536pfvbz6UWbkAwAAAAAAAICvjB81WWYnfcwhvl876evYSR9ztd+vnfT17KSPucrv1076PjftpI/dbJPPTvo+N+2kj91sk89O+j437aSP3WyTz1tNIFAQCBQEAgWBQEEgUBAIFAQCBYFAQSBQEAgUBAIFgUBBIFAQCBQEAgWBQEEgUBAIFAQCO+n73LSTPnazTT476fvctJM+drNNPjvp+9y0kz52s00+O+nr2Ekfc7Xfr5dXL7OTPubw3y8AAAAAAAAAfH38qMkyO+ljDvH92klfx076mKv9fu2kr2cnfcxVfr920ve5aSd97GabfHbS97lpJ33sZpt8dtL3uWknfexmm3zeagKBgkCgIBAoCAQKAoGCQKAgECgIBAoCgYJAoCAQKAgECgKBgkCgIBAoCAQKAoGCQKAgENhJ3+emnfSxm23y2Unf56ad9LGbbfLZSd/npp30sZtt8tlJX8dO+pir/X69vHqZnfQxh/9+AQAAAAAAAODrE3/UpPvOt3x20geM7aR33/mW799TZSd91Lad9O473/LdP1l20kdt2knvvvMt31120sdt2knvvvMt39jNNjvkE56dZdNOevedb/nus5M+xk46bKUgECgIBAoCgYJAoCAQKAgECgKBgkCgIBAoCAQKAoGCQKAgECgIBAoCgYJAoCAQKAgESwXpvvMt33120sds2knvvvMt39jNNjvkE56dZdNOevedb/nuspM+bv1Oevedb/nusJM+7nE76d13vuWzkz7ATjoAAAAAAAAAbGYnfdnh81X/H+X43/PZSV9xqg6Yr5rskAct8tlJX+dQ+arRDvmCNvnspK9ztHxtdsgnPDuLnfRBR8rXZod8wrOz2EkfdKR8bXbIJzw7i5102EpBIFAQCBQEAgWBQEEgUBAIFAQCBYFAQSBQEAgUBAIFgUBBIFAQCBQEAgWBQEEgUBAI7KSvd6R8bXbIJzw7i530QUfK12aHfMKzs9hJH3C0fG12yCc8O4ud9Ec6XL5qtEO+oE0+O+nLDp+vGrwc+gHd8wEAAACX8jdzZLbl0C/0iwAAAABJRU5ErkJggg==" }, - "eigthnote": { + "eighthnote": { "icon": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAADICAYAAACtWK6eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFM0lEQVR4nO3dQY4VVRgF4P+ZOCHMQRfgBliAcU8wMUx1gnsyOmcDLgBkTpgweA66JXa6799V/W61J5fvm1pwT27lgCSdOlUAAABAhNM9//1pVT2rqidV9e3ksz9X1aeq+lBVHx/4e0TnO5/PP1bVq6p6UVXP50Wrqqq/q+ptVb05nU5/PPD3iL6/CsjXFeR5VX0/OdTIu7p64XtE5zufzz9X1S/HxLl5VFW9Pp1Ov+78ddH3VyH5RgV5WlU/HBbnbn/V9j9povNd/83xe93/N/Qs56r6acffJNH3V0H5vhk8/OzYLBefmZ7vVT1eOer6rJc7nk+/v5h8o4I8OTDIyJ4z0/O9OCzFnDPT7y8m36ggs/9BtMWeM9Pzzf4H+Rbf7Xg2/f5i8o0KApSCQEtBoKEg0FAQaCgINBQEGgoCDQWBhoJAQ0GgoSDQUBBoKAg0FAQaCgINBYGGgkBDQaAxKsjnR02x/8z0fHu/ATXD+x3Ppt9fTL5RQT4dGGRkz5np+d4elmLOmen3F5NvVJAPBwYZ2XNmer43dfUxt8dyvj5zq/T7i8k3KsjHuvoc42N5V/u+3xqd7/oLh6/rcUry76dH/9zxa6Lvr4Ly+Xj12KyPV7+sq4+67flu1Rbv6+p/q37z8eoHmZEPAAAAAAAAAL4yftRkbPmd9PR8FfB+7aRvs9xOenq+Cnm/dtK3W2YnPT1fBb1fO+nHnJm+k56eL+b92kk/5sz0nfT0fDHv1076MWem76Sn54t5v75qAg0FgYaCQENBoKEg0FAQaCgINBQEGgoCDQWBhoJAQ0GgoSDQUBBoKAg0FAQaCgINBYGGgkDDTvoxZ6bvpKfni3m/dtKPOTN9Jz09X8z7tZN+zJnpO+np+WLer530bZbaSU/PV0Hv18erx5bfSU/PV+HvFwAAAAAAAAC+Pn7UZGz5nfRyf/fen530bZbbSS/39+Woau7PTvp2y+ykl/u7dWQN7s9O+jFn2iG/7MyY+7OTfsyZdsgvOzPm/uykH3OmHfLLzoy5P181gYaCQENBoKEg0FAQaCgINBQEGgoCDQWBhoJAQ0GgoSDQUBBoKAg0FAQaCgINBYGGgkBDQaBhJ/2YM+2QX3ZmzP3ZST/mTDvkl50Zc3920o850w75ZWfG3J+d9G2W2kkv9/df7f35ePXY8jvp5f4uvT8AAAAAAAAA+Mq0P2qSsFPdkW/tnfQKyDcsSMpO9fAXyfflqFpwJ71C8t1ZkKSd6jsflu/WkbXQTnoF5Rv9uHvMTvWAfDettpMek29UkJid6gnPzrJSvvSd9Jh8o4LE7FQPyHfbSjvpMfl81QQaCgINBYGGgkBDQaChINBQEGgoCDQUBBoKAg0FgYaCQENBoKEg0FAQaCgINBQEGgoCDQWBxqggMTvVA/LdttJOeky+UUFidqonPDvLSvnSd9Jj8o0KErNTPSDfTavtpMfku7MgSTvVd5HvhuV20iso35aPV8fuVMu39k565ecDAAAAAAAAgK+MnfSx5fNV/o9y/O/57KRvOKoWzFchO+SNiHx20rdZKl8F7ZAPxOSzk77NavlidsgnPDuLnfQLrZQvZod8wrOz2Em/0Er5YnbIJzw7i5102EtBoKEg0FAQaCgINBQEGgoCDQWBhoJAQ0GgoSDQUBBoKAg0FAQaCgINBYGGgkBDQaChINCwk77dSvlidsgnPDuLnfQLrZQvZod8wrOz2Em/wGr5YnbIJzw7i530B1ouXwXtkA/E5LOTPrZ8vgr4OPQ90vMBAAAAj+Uf73PYeRwCFc4AAAAASUVORK5CYII=" }, "pitchfork": { diff --git a/libs/core/icons.ts b/libs/core/icons.ts index 4dd6817d2d0..758d9e2bc59 100644 --- a/libs/core/icons.ts +++ b/libs/core/icons.ts @@ -114,8 +114,12 @@ enum IconNames { //% jres=icons.quarternote QuarterNote, //% block="eigth note" - //% jres=icons.eigthnote + //% jres=icons.eighthnote + //% deprecated=true blockHidden=true EigthNote, + //% block="eighth note" + //% jres=icons.eighthnote + EighthNote, //% block="pitchfork" //% jres=icons.pitchfork Pitchfork, @@ -196,6 +200,7 @@ namespace basic { //% weight=50 blockGap=8 //% blockId=basic_show_arrow //% block="show arrow %i=device_arrow" + //% direction.label="direction" //% parts="ledmatrix" //% help=basic/show-arrow export function showArrow(direction: number, interval = 600) { @@ -508,6 +513,12 @@ namespace images { . . # . . # # # . . # # # . .`); + case IconNames.EighthNote: return images.createImage(` + . . # . . + . . # # . + . . # . # + # # # . . + # # # . .`); case IconNames.EigthNote: return images.createImage(` . . # . . . . # # . diff --git a/libs/core/images.cpp b/libs/core/images.cpp index f3921a32efd..d548853ba6b 100644 --- a/libs/core/images.cpp +++ b/libs/core/images.cpp @@ -87,6 +87,7 @@ void plotImage(Image i, int xOffset = 0) { */ //% help=images/show-image weight=80 blockNamespace=images //% blockId=device_show_image_offset block="show image %sprite(myImage)|at offset %offset ||and interval (ms) %interval" +//% sprite.label="image" xOffset.label="offset" interval.label="interval" //% interval.defl=400 //% blockGap=8 parts="ledmatrix" async void showImage(Image sprite, int xOffset, int interval = 400) { @@ -112,6 +113,7 @@ void plotFrame(Image i, int xOffset) { //% help=images/scroll-image weight=79 async blockNamespace=images //% blockId=device_scroll_image //% block="scroll image %sprite(myImage)|with offset %frameoffset|and interval (ms) %delay" +//% id.label="image" frameOffset.label="offset" interval.label="interval" //% blockGap=8 parts="ledmatrix" void scrollImage(Image id, int frameOffset, int interval) { MicroBitImage i(id->img); diff --git a/libs/core/input.cpp b/libs/core/input.cpp index 80b1d025b08..2eb718e7c19 100644 --- a/libs/core/input.cpp +++ b/libs/core/input.cpp @@ -345,13 +345,15 @@ namespace input { * Get the magnetic force value in ``micro-Teslas`` (``ÂĩT``). This function is not supported in the simulator. * @param dimension the x, y, or z dimension, eg: Dimension.X */ - //% help=input/magnetic-force weight=51 + //% help=input/magnetic-force weight=54 //% blockId=device_get_magnetic_force block="magnetic force (ÂĩT)|%NAME" blockGap=8 //% parts="compass" //% advanced=true TNumber magneticForce(Dimension dimension) { + /* https://github.com/microsoft/pxt-microbit/issues/4995 if (!uBit.compass.isCalibrated()) uBit.compass.calibrate(); + */ double d = 0; switch (dimension) { case Dimension::X: d = uBit.compass.getX(); break; @@ -367,7 +369,7 @@ namespace input { */ //% help=input/calibrate-compass advanced=true //% blockId="input_compass_calibrate" block="calibrate compass" - //% weight=45 + //% weight=55 void calibrateCompass() { uBit.compass.calibrate(); } diff --git a/libs/core/led.cpp b/libs/core/led.cpp index 1815c05d7cb..4d8d4ba5455 100644 --- a/libs/core/led.cpp +++ b/libs/core/led.cpp @@ -20,6 +20,7 @@ namespace led { */ //% help=led/plot weight=78 //% blockId=device_plot block="plot|x %x|y %y" blockGap=8 + //% x.label="x" y.label="y" //% parts="ledmatrix" //% x.min=0 x.max=4 y.min=0 y.max=4 //% x.fieldOptions.precision=1 y.fieldOptions.precision=1 @@ -35,6 +36,7 @@ namespace led { */ //% help=led/plot-brightness weight=78 //% blockId=device_plot_brightness block="plot|x %x|y %y|brightness %brightness" blockGap=8 + //% x.label="x" y.label="y" brightness.label="brightness" //% parts="ledmatrix" //% x.min=0 x.max=4 y.min=0 y.max=4 brightness.min=0 brightness.max=255 //% x.fieldOptions.precision=1 y.fieldOptions.precision=1 @@ -54,6 +56,7 @@ namespace led { */ //% help=led/unplot weight=77 //% blockId=device_unplot block="unplot|x %x|y %y" blockGap=8 + //% x.label="x" y.label="y" //% parts="ledmatrix" //% x.min=0 x.max=4 y.min=0 y.max=4 //% x.fieldOptions.precision=1 y.fieldOptions.precision=1 @@ -68,6 +71,7 @@ namespace led { */ //% help=led/point-brightness weight=76 //% blockId=device_point_brightness block="point|x %x|y %y brightness" + //% x.label="x" y.label="y" //% parts="ledmatrix" //% x.min=0 x.max=4 y.min=0 y.max=4 //% x.fieldOptions.precision=1 y.fieldOptions.precision=1 @@ -93,6 +97,7 @@ namespace led { */ //% help=led/set-brightness weight=59 //% blockId=device_set_brightness block="set brightness %value" + //% value.label="value" //% parts="ledmatrix" //% advanced=true //% value.min=0 value.max=255 @@ -134,6 +139,7 @@ namespace led { * Turns on or off the display */ //% help=led/enable blockId=device_led_enable block="led enable %on" + //% on.label="value" //% advanced=true parts="ledmatrix" void enable(bool on) { if (on) uBit.display.enable(); diff --git a/libs/core/led.ts b/libs/core/led.ts index e3911ce9798..bd32cece9b2 100644 --- a/libs/core/led.ts +++ b/libs/core/led.ts @@ -1,7 +1,7 @@ /** * Control of the LED screen. */ -//% color=#5C2D91 weight=101 icon="\uf205" +//% color=#5C2D91 weight=101 icon="\uf205" block="LED" namespace led { /** * Get the on/off state of the specified LED using x, y coordinates. (0,0) is upper left. @@ -10,6 +10,7 @@ namespace led { */ //% help=led/point weight=76 //% blockId=device_point block="point|x %x|y %y" + //% x.label="x" y.label="y" //% parts="ledmatrix" //% x.min=0 x.max=4 y.min=0 y.max=4 //% x.fieldOptions.precision=1 y.fieldOptions.precision=1 @@ -22,18 +23,35 @@ namespace led { // when was the current high value recorded let barGraphHighLast = 0; + /** + * Controls where plotbargraph prints to the console + **/ + export let barGraphToConsole = true + /** * Displays a vertical bar graph based on the `value` and `high` value. * If `high` is 0, the chart gets adjusted automatically. * @param value current value to plot * @param high maximum value. If 0, maximum value adjusted automatically, eg: 0 + * @param valueToConsole if true, prints value to the serial port */ //% help=led/plot-bar-graph weight=20 - //% blockId=device_plot_bar_graph block="plot bar graph of %value up to %high" icon="\uf080" blockExternalInputs=true + //% blockId=device_plot_bar_graph block="plot bar graph of $value up to $high|| serial write $valueToConsole" icon="\uf080" blockExternalInputs=true + //% value.label="value" high.label="maximum" valueToConsole.label="serial write" //% parts="ledmatrix" - export function plotBarGraph(value: number, high: number): void { + //% valueToConsole.shadow=toggleOnOff + //% valueToConsole.defl=true + export function plotBarGraph(value: number, high: number, valueToConsole?: boolean): void { + if (valueToConsole == undefined) { + valueToConsole = barGraphToConsole; + } const now = input.runningTime(); - console.logValue("", value); + if (valueToConsole) + console.logValue("", value); + if (isNaN(value)) { + basic.clearScreen() + return + } value = Math.abs(value); // auto-scale "high" is not provided @@ -73,6 +91,7 @@ namespace led { */ //% help=led/toggle weight=77 //% blockId=device_led_toggle block="toggle|x %x|y %y" icon="\uf204" blockGap=8 + //% x.label="x" y.label="y" //% parts="ledmatrix" //% x.min=0 x.max=4 y.min=0 y.max=4 //% x.fieldOptions.precision=1 y.fieldOptions.precision=1 @@ -112,7 +131,7 @@ namespace led { /** * Fades in the screen display. - * @param ms fade time in milleseconds + * @param ms fade time in milliseconds */ //% help=led/fade-in //% parts="ledmatrix" diff --git a/libs/core/logo.cpp b/libs/core/logo.cpp index f11f26343d3..966222b264d 100644 --- a/libs/core/logo.cpp +++ b/libs/core/logo.cpp @@ -26,7 +26,7 @@ namespace input { //% help="input/on-logo-event" void onLogoEvent(TouchButtonEvent action, Action body) { #if MICROBIT_CODAL - registerWithDal(uBit.logo.id, action, body); + registerWithDal(uBit.io.logo.id, action, body); #else target_panic(PANIC_VARIANT_NOT_SUPPORTED); #endif @@ -43,7 +43,7 @@ namespace input { //% help="input/logo-is-pressed" bool logoIsPressed() { #if MICROBIT_CODAL - return uBit.logo.isPressed(); + return uBit.io.logo.isTouched(); #else target_panic(PANIC_VARIANT_NOT_SUPPORTED); return false; diff --git a/libs/core/loops.ts b/libs/core/loops.ts new file mode 100644 index 00000000000..5691eeea57a --- /dev/null +++ b/libs/core/loops.ts @@ -0,0 +1,37 @@ +namespace loops { + /** + * Repeats the code forever in the background. + * After each iteration, allows other codes to run for a set duration + * so that it runs on a timer + * @param interval time (in ms) to wait between each iteration of the action. + * @param body code to execute + */ + //% weight=45 blockAllowMultiple=1 + //% interval.shadow=longTimePicker + //% afterOnStart=true help=loops/every-interval + //% blockId=every_interval block="every $interval ms" + //% interval.label="value" + export function everyInterval(interval: number, a: () => void): void { + control.runInParallel(() => { + let start = 0; + while (true) { + start = control.millis(); + a(); + pause(Math.max(0, interval - (control.millis() - start))); + } + }); + } + + /** + * Get the time field editor + * @param ms time duration in milliseconds, eg: 500, 1000 + */ + //% blockId=longTimePicker block="%ms" + //% blockHidden=true shim=TD_ID + //% colorSecondary="#FFFFFF" + //% ms.fieldEditor="numberdropdown" ms.fieldOptions.decompileLiterals=true + //% ms.fieldOptions.data='[["100 ms", 100], ["200 ms", 200], ["500 ms", 500], ["1 second", 1000], ["1 minute", 60000], ["1 hour", 3600000]]' + export function __timePicker(ms: number): number { + return ms; + } +} \ No newline at end of file diff --git a/libs/core/melodies.ts b/libs/core/melodies.ts index e8663463e04..26936ef8541 100644 --- a/libs/core/melodies.ts +++ b/libs/core/melodies.ts @@ -69,51 +69,57 @@ enum Melodies { } namespace music { - export function getMelody(melody: Melodies): string[] { + return _bufferToMelody(_getMelodyBuffer(melody)); + } + + // The buffer format is 2 bytes per note. First note byte is midi + // note number, second byte is duration in quarter beats. The note + // number 0 is reserved for rests + export function _getMelodyBuffer(melody: Melodies) { switch (melody) { case Melodies.Dadadadum: - return ['r4:2', 'g', 'g', 'g', 'eb:8', 'r:2', 'f', 'f', 'f', 'd:8']; + return hex`00024f024f024f024b0800024d024d024d024a08`; case Melodies.Entertainer: - return ['d4:1', 'd#', 'e', 'c5:2', 'e4:1', 'c5:2', 'e4:1', 'c5:3', 'c:1', 'd', 'd#', 'e', 'c', 'd', 'e:2', 'b4:1', 'd5:2', 'c:4']; + return hex`4a014b014c0154024c0154024c0154035401560157015801540156015802530156025404`; case Melodies.Prelude: - return ['c4:1', 'e', 'g', 'c5', 'e', 'g4', 'c5', 'e', 'c4', 'e', 'g', 'c5', 'e', 'g4', 'c5', 'e', 'c4', 'd', 'g', 'd5', 'f', 'g4', 'd5', 'f', 'c4', 'd', 'g', 'd5', 'f', 'g4', 'd5', 'f', 'b3', 'd4', 'g', 'd5', 'f', 'g4', 'd5', 'f', 'b3', 'd4', 'g', 'd5', 'f', 'g4', 'd5', 'f', 'c4', 'e', 'g', 'c5', 'e', 'g4', 'c5', 'e', 'c4', 'e', 'g', 'c5', 'e', 'g4', 'c5', 'e']; + return hex`48014c014f01540158014f015401580148014c014f01540158014f015401580148014a014f01560159014f015601590148014a014f01560159014f015601590147014a014f01560159014f015601590147014a014f01560159014f015601590148014c014f01540158014f015401580148014c014f01540158014f0154015801`; case Melodies.Ode: - return ['e4', 'e', 'f', 'g', 'g', 'f', 'e', 'd', 'c', 'c', 'd', 'e', 'e:6', 'd:2', 'd:8', 'e:4', 'e', 'f', 'g', 'g', 'f', 'e', 'd', 'c', 'c', 'd', 'e', 'd:6', 'c:2', 'c:8']; + return hex`4c044c044d044f044f044d044c044a04480448044a044c044c064a024a084c044c044d044f044f044d044c044a04480448044a044c044a0648024808`; case Melodies.Nyan: - return ['f#5:2', 'g#', 'c#:1', 'd#:2', 'b4:1', 'd5:1', 'c#', 'b4:2', 'b', 'c#5', 'd', 'd:1', 'c#', 'b4:1', 'c#5:1', 'd#', 'f#', 'g#', 'd#', 'f#', 'c#', 'd', 'b4', 'c#5', 'b4', 'd#5:2', 'f#', 'g#:1', 'd#', 'f#', 'c#', 'd#', 'b4', 'd5', 'd#', 'd', 'c#', 'b4', 'c#5', 'd:2', 'b4:1', 'c#5', 'd#', 'f#', 'c#', 'd', 'c#', 'b4', 'c#5:2', 'b4', 'c#5', 'b4', 'f#:1', 'g#', 'b:2', 'f#:1', 'g#', 'b', 'c#5', 'd#', 'b4', 'e5', 'd#', 'e', 'f#', 'b4:2', 'b', 'f#:1', 'g#', 'b', 'f#', 'e5', 'd#', 'c#', 'b4', 'f#', 'd#', 'e', 'f#', 'b:2', 'f#:1', 'g#', 'b:2', 'f#:1', 'g#', 'b', 'b', 'c#5', 'd#', 'b4', 'f#', 'g#', 'f#', 'b:2', 'b:1', 'a#', 'b', 'f#', 'g#', 'b', 'e5', 'd#', 'e', 'f#', 'b4:2', 'c#5']; + return hex`5a025c02550157025301560155015302530255025602560155015301550157015a015c0157015a015501560153015501530157025a025c0157015a0155015701530156015701560155015301550156025301550157015a01550156015501530155025302550253024e01500153024e01500153015501570153015801570158015a01530253024e01500153014e0158015701550153014e014b014c014e0153024e01500153024e015001530153015501570153014e0150014e0153025301520153014e01500153015801570158015a0153025502`; case Melodies.Ringtone: - return ['c4:1', 'd', 'e:2', 'g', 'd:1', 'e', 'f:2', 'a', 'e:1', 'f', 'g:2', 'b', 'c5:4']; + return hex`48014a014c024f024a014c014d0251024c014d014f0253025404`; case Melodies.Funk: - return ['c2:2', 'c', 'd#', 'c:1', 'f:2', 'c:1', 'f:2', 'f#', 'g', 'c', 'c', 'g', 'c:1', 'f#:2', 'c:1', 'f#:2', 'f', 'd#']; + return hex`300230023302300135023001350236023702300230023702300136023001360235023302`; case Melodies.Blues: - return ['c2:2', 'e', 'g', 'a', 'a#', 'a', 'g', 'e', 'c2:2', 'e', 'g', 'a', 'a#', 'a', 'g', 'e', 'f', 'a', 'c3', 'd', 'd#', 'd', 'c', 'a2', 'c2:2', 'e', 'g', 'a', 'a#', 'a', 'g', 'e', 'g', 'b', 'd3', 'f', 'f2', 'a', 'c3', 'd#', 'c2:2', 'e', 'g', 'e', 'g', 'f', 'e', 'd']; + return hex`30023402370239023a0239023702340230023402370239023a02390237023402350239023c023e023f023e023c02390230023402370239023a0239023702340237023b023e024102350239023c023f0230023402370234023702350234023202`; case Melodies.Birthday: - return ['c4:3', 'c:1', 'd:4', 'c:4', 'f', 'e:8', 'c:3', 'c:1', 'd:4', 'c:4', 'g', 'f:8', 'c:3', 'c:1', 'c5:4', 'a4', 'f', 'e', 'd', 'a#:3', 'a#:1', 'a:4', 'f', 'g', 'f:8']; + return hex`480348014a0448044d044c08480348014a0448044f044d0848034801540451044d044c044a045203520151044d044f044d08`; case Melodies.Wedding: - return ['c4:4', 'f:3', 'f:1', 'f:8', 'c:4', 'g:3', 'e:1', 'f:8', 'c:4', 'f:3', 'a:1', 'c5:4', 'a4:3', 'f:1', 'f:4', 'e:3', 'f:1', 'g:8']; + return hex`48044d034d014d0848044f034c014d0848044d035101540451034d014d044c034d014f08`; case Melodies.Funeral: - return ['c3:4', 'c:3', 'c:1', 'c:4', 'd#:3', 'd:1', 'd:3', 'c:1', 'c:3', 'b2:1', 'c3:4']; + return hex`3c043c033c013c043f033e013e033c013c033b013c04`; case Melodies.Punchline: - return ['c4:3', 'g3:1', 'f#', 'g', 'g#:3', 'g', 'r', 'b', 'c4']; + return hex`480343014201430144034303000347034803`; case Melodies.Baddy: - return ['c3:3', 'r', 'd:2', 'd#', 'r', 'c', 'r', 'f#:8']; + return hex`3c0300033e023f0200023c0200024208`; case Melodies.Chase: - return ['a4:1', 'b', 'c5', 'b4', 'a:2', 'r', 'a:1', 'b', 'c5', 'b4', 'a:2', 'r', 'a:2', 'e5', 'd#', 'e', 'f', 'e', 'd#', 'e', 'b4:1', 'c5', 'd', 'c', 'b4:2', 'r', 'b:1', 'c5', 'd', 'c', 'b4:2', 'r', 'b:2', 'e5', 'd#', 'e', 'f', 'e', 'd#', 'e']; + return hex`5101530154015301510200025101530154015301510200025102580257025802590258025702580253015401560154015302000253015401560154015302000253025802570258025902580257025802`; case Melodies.BaDing: - return ['b5:1', 'e6:3']; + return hex`5f016403`; case Melodies.Wawawawaa: - return ['e3:3', 'r:1', 'd#:3', 'r:1', 'd:4', 'r:1', 'c#:8']; + return hex`400300013f0300013e0400013d08`; case Melodies.JumpUp: - return ['c5:1', 'd', 'e', 'f', 'g']; + return hex`54015601580159015b01`; case Melodies.JumpDown: - return ['g5:1', 'f', 'e', 'd', 'c']; + return hex`5b015901580156015401`; case Melodies.PowerUp: - return ['g4:1', 'c5', 'e', 'g:2', 'e:1', 'g:3']; + return hex`4f01540158015b0258015b03`; case Melodies.PowerDown: - return ['g5:1', 'd#', 'c', 'g4:2', 'b:1', 'c5:3']; - default: - return []; + return hex`5b01570154014f0253015403`; + + default: return undefined; } } } \ No newline at end of file diff --git a/libs/core/music.cpp b/libs/core/music.cpp index e1477be5ced..86b367817c6 100644 --- a/libs/core/music.cpp +++ b/libs/core/music.cpp @@ -11,6 +11,7 @@ namespace music { * @param volume the volume 0...255 */ //% blockId=synth_set_volume block="set volume %volume" +//% volume.label="value" //% volume.min=0 volume.max=255 //% volume.defl=127 //% help=music/set-volume @@ -47,11 +48,12 @@ int volume() { * @param enabled whether the built-in speaker is enabled in addition to the sound pin */ //% blockId=music_set_built_in_speaker_enable block="set built-in speaker $enabled" -//% blockGap=8 +//% enabled.label="value" //% group="micro:bit (V2)" //% parts=builtinspeaker //% help=music/set-built-in-speaker-enabled //% enabled.shadow=toggleOnOff +//% weight=0 void setBuiltInSpeakerEnabled(bool enabled) { #if MICROBIT_CODAL uBit.audio.setSpeakerEnabled(enabled); @@ -63,6 +65,26 @@ void setBuiltInSpeakerEnabled(bool enabled) { #endif } +/** +* Check whether any sound is being played, no matter the source +*/ +//% blockId=music_sound_is_playing block="sound is playing" +//% group="micro:bit (V2)" +//% help=music/is-sound-playing +//% weight=0 +bool isSoundPlaying() { +#if MICROBIT_CODAL + if (uBit.audio.mixer.getSilenceStartTime() == 0) { + return false; + } else { + return uBit.audio.isPlaying(); + } + +#else + target_panic(PANIC_VARIANT_NOT_SUPPORTED); +#endif +} + /** * Defines an optional sample level to generate during periods of silence. **/ diff --git a/libs/core/music.ts b/libs/core/music.ts index 3a10764e860..d0aa455222b 100644 --- a/libs/core/music.ts +++ b/libs/core/music.ts @@ -113,12 +113,16 @@ enum BeatFraction { //% block=1 Whole = 1, //% block="1/2" + //% ariaLabel="one half" Half = 2, //% block="1/4" + //% ariaLabel="one quarter" Quarter = 4, //% block="1/8" + //% ariaLabel="one eighth" Eighth = 8, //% block="1/16" + //% ariaLabel="one sixteenth" Sixteenth = 16, //% block="2" Double = 32, @@ -178,6 +182,7 @@ namespace music { const INTERNAL_MELODY_ENDED = 5; let beatsPerMinute: number = 120; + let stopSoundHandlers: (() => void)[]; //% whenUsed const freqs = hex` 1f00210023002500270029002c002e003100340037003a003e004100450049004e00520057005c00620068006e00 @@ -195,10 +200,13 @@ namespace music { */ //% help=music/play-tone weight=90 //% blockId=device_play_note block="play|tone %note=device_note|for %duration=device_beat" blockGap=8 + //% frequency.label="note" ms.label="duration" //% parts="headphone" //% useEnumVal=1 //% group="Tone" + //% deprecated=1 export function playTone(frequency: number, ms: number): void { + if (isNaN(frequency) || isNaN(ms)) return; if (_playTone) _playTone(frequency, ms); else pins.analogPitch(frequency, ms); } @@ -209,6 +217,7 @@ namespace music { */ //% help=music/ring-tone weight=80 //% blockId=device_ring block="ring tone (Hz)|%note=device_note" blockGap=8 + //% frequency.label="note" //% parts="headphone" //% useEnumVal=1 //% group="Tone" @@ -221,7 +230,8 @@ namespace music { * @param ms rest duration in milliseconds (ms) */ //% help=music/rest weight=79 - //% blockId=device_rest block="rest(ms)|%duration=device_beat" + //% blockId=device_rest block="rest for |%duration=device_beat" + //% ms.label="value" //% parts="headphone" //% group="Tone" export function rest(ms: number): void { @@ -288,10 +298,12 @@ namespace music { */ //% help=music/change-tempo-by weight=39 //% blockId=device_change_tempo block="change tempo by (bpm)|%value" blockGap=8 + //% bpm.label="value" //% group="Tempo" //% weight=100 export function changeTempoBy(bpm: number): void { init(); + if (isNaN(bpm)) return; setTempo(beatsPerMinute + bpm); } @@ -301,31 +313,48 @@ namespace music { */ //% help=music/set-tempo weight=38 //% blockId=device_set_tempo block="set tempo to (bpm)|%value" - //% bpm.min=4 bpm.max=400 + //% bpm.label="value" + //% bpm.min=40 bpm.max=500 //% group="Tempo" //% weight=99 export function setTempo(bpm: number): void { init(); + if (isNaN(bpm)) return; if (bpm > 0) { beatsPerMinute = Math.max(1, bpm); } } - let currentMelody: Melody; - let currentBackgroundMelody: Melody; + let currentMelody: MelodyReader; + let currentBackgroundMelody: MelodyReader; /** * Gets the melody array of a built-in melody. * @param name the note name, eg: Note.C */ - //% weight=50 help=music/builtin-melody + //% weight=50 help=music/built-in-melody //% blockId=device_builtin_melody block="%melody" //% blockHidden=true //% group="Melody Advanced" + //% deprecated=1 export function builtInMelody(melody: Melodies): string[] { return getMelody(melody); } + /** + * Gets the melody array of a built-in melody. + * @param melody the melody name + */ + //% weight=60 help=music/built-in-playable-melody + //% blockId=device_builtin_melody_playable block="melody|$melody" + //% toolboxParent=music_playable_play_default_bkg + //% toolboxParentArgument=toPlay + //% duplicateShadowOnDrag + //% group="Melody Advanced" + export function builtInPlayableMelody(melody: Melodies): StringArrayPlayable { + return new StringArrayPlayable(getMelody(melody), undefined); + } + /** * Registers code to run on various melody events */ @@ -343,7 +372,7 @@ namespace music { //% parts="headphone" //% group="Melody Advanced" export function beginMelody(melodyArray: string[], options: MelodyOptions = 1) { - return startMelody(melodyArray, options); + return startMelodyInternal(melodyArray, options); } /** @@ -354,62 +383,48 @@ namespace music { */ //% help=music/begin-melody weight=60 blockGap=16 //% blockId=device_start_melody block="start melody %melody=device_builtin_melody| repeating %options" + //% melodyArray.label="melody" //% parts="headphone" //% group="Melody Advanced" + //% deprecated=1 export function startMelody(melodyArray: string[], options: MelodyOptions = 1) { - init(); - if (currentMelody != undefined) { - if (((options & MelodyOptions.OnceInBackground) == 0) - && ((options & MelodyOptions.ForeverInBackground) == 0) - && currentMelody.background) { - currentBackgroundMelody = currentMelody; - currentMelody = null; - control.raiseEvent(MICROBIT_MELODY_ID, MusicEvent.BackgroundMelodyPaused); - } - if (currentMelody) - control.raiseEvent(MICROBIT_MELODY_ID, currentMelody.background ? MusicEvent.BackgroundMelodyEnded : MusicEvent.MelodyEnded); - currentMelody = new Melody(melodyArray, options); - control.raiseEvent(MICROBIT_MELODY_ID, currentMelody.background ? MusicEvent.BackgroundMelodyStarted : MusicEvent.MelodyStarted); - } else { - currentMelody = new Melody(melodyArray, options); - control.raiseEvent(MICROBIT_MELODY_ID, currentMelody.background ? MusicEvent.BackgroundMelodyStarted : MusicEvent.MelodyStarted); - // Only start the fiber once - control.inBackground(() => { - while (currentMelody.hasNextNote()) { - playNextNote(currentMelody); - if (!currentMelody.hasNextNote() && currentBackgroundMelody) { - // Swap the background melody back - currentMelody = currentBackgroundMelody; - currentBackgroundMelody = null; - control.raiseEvent(MICROBIT_MELODY_ID, MusicEvent.MelodyEnded); - control.raiseEvent(MICROBIT_MELODY_ID, MusicEvent.BackgroundMelodyResumed); - control.raiseEvent(MICROBIT_MELODY_ID, INTERNAL_MELODY_ENDED); - } - } - control.raiseEvent(MICROBIT_MELODY_ID, currentMelody.background ? MusicEvent.BackgroundMelodyEnded : MusicEvent.MelodyEnded); - if (!currentMelody.background) - control.raiseEvent(MICROBIT_MELODY_ID, INTERNAL_MELODY_ENDED); - currentMelody = null; - }) - } + return startMelodyInternal(melodyArray, options); } - /** * Play a melody from the melody editor. - * @param melody - string of up to eight notes [C D E F G A B C5] or rests [-] separated by spaces, which will be played one at a time, ex: "E D G F B A C5 B " - * @param tempo - number in beats per minute (bpm), dictating how long each note will play for + * @param melody string of up to eight notes [C D E F G A B C5] or rests [-] separated by spaces, which will be played one at a time, ex: "E D G F B A C5 B " + * @param tempo number in beats per minute (bpm), dictating how long each note will play for */ //% block="play melody $melody at tempo $tempo|(bpm)" blockId=playMelody + //% melody.label="melody" tempo.label="tempo" //% weight=85 blockGap=8 help=music/play-melody //% melody.shadow="melody_editor" //% tempo.min=40 tempo.max=500 //% tempo.defl=120 //% parts=headphone //% group="Melody" + //% deprecated=1 export function playMelody(melody: string, tempo: number) { melody = melody || ""; setTempo(tempo); + + const playable = new StringArrayPlayable(melody, tempo); + playable._play(PlaybackMode.UntilDone); + } + + // deprecated, use _startMelodyInternal instead + export function startMelodyInternal(melodyArray: string[], options: MelodyOptions) { + const reader = new MelodyArrayReader(melodyArray); + _startMelodyInternal(reader, options); + } + + export function waitForMelodyEnd() { + control.waitForEvent(MICROBIT_MELODY_ID, INTERNAL_MELODY_ENDED); + } + + // deprecated, use StringArrayPlayable instead + export function getMelodyNotes(melody: string) { let notes: string[] = melody.split(" ").filter(n => !!n); let newOctave = false; @@ -426,8 +441,13 @@ namespace music { } } - music.startMelody(notes, MelodyOptions.Once) - control.waitForEvent(MICROBIT_MELODY_ID, INTERNAL_MELODY_ENDED); + // Switch back to octave 4 on first note if repeating and final note is octave 5. + // Otherwise the higher octave will persist. + if (notes[notes.length - 1] === "C5" && notes[0] != "C5") { + notes[0] += "4"; + } + + return notes; } /** @@ -463,6 +483,13 @@ namespace music { startMelody([], MelodyOptions.Once); } + export function _onStopSound(handler: () => void) { + if (!stopSoundHandlers) { + stopSoundHandlers = []; + } + stopSoundHandlers.push(handler); + } + /** * Stop all sounds and melodies currently playing. */ @@ -474,6 +501,12 @@ namespace music { rest(0); stopMelody(MelodyStopOptions.All); music.__stopSoundExpressions(); + _stopPlayables(); + if (stopSoundHandlers) { + for (const handler of stopSoundHandlers) { + handler() + } + } } @@ -487,83 +520,333 @@ namespace music { _playTone = f; } - function playNextNote(melody: Melody): void { - // cache elements - let currNote = melody.nextNote(); - let currentPos = melody.currentPos; - let currentDuration = melody.currentDuration; - let currentOctave = melody.currentOctave; - - let note: number; - let isrest: boolean = false; - let beatPos: number; - let parsingOctave: boolean = true; - let prevNote: boolean = false; - - for (let pos = 0; pos < currNote.length; pos++) { - let noteChar = currNote.charAt(pos); - switch (noteChar) { - case 'c': case 'C': note = 1; prevNote = true; break; - case 'd': case 'D': note = 3; prevNote = true; break; - case 'e': case 'E': note = 5; prevNote = true; break; - case 'f': case 'F': note = 6; prevNote = true; break; - case 'g': case 'G': note = 8; prevNote = true; break; - case 'a': case 'A': note = 10; prevNote = true; break; - case 'B': note = 12; prevNote = true; break; - case 'r': case 'R': isrest = true; prevNote = false; break; - case '#': note++; prevNote = false; break; - case 'b': if (prevNote) note--; else { note = 12; prevNote = true; } break; - case ':': parsingOctave = false; beatPos = pos; prevNote = false; break; - default: prevNote = false; if (parsingOctave) currentOctave = parseInt(noteChar); - } + /** + * Converts an octave and note offset into an integer frequency. + * Returns 0 if the note is out of range. + * + * @param octave The octave of the note (1 - 8) + * @param note The offset of the note within the octave + * @returns A frequency in HZ or 0 if out of range + */ + export function getFrequencyForNote(octave: number, note: number) { + const index = (note + (12 * (octave - 1))) << 1; + + if (index >= freqs.length) { + return Math.round(440 * Math.pow(2, (((octave + 1) * 12 + note) - 70) / 12)); } - if (!parsingOctave) { - currentDuration = parseInt(currNote.substr(beatPos + 1, currNote.length - beatPos)); + else { + return freqs.getNumber(NumberFormat.UInt16LE, index) || 0; } + } + + function playNextNote(melody: MelodyReader): void { + melody.readNote(); let beat = Math.idiv(60000, beatsPerMinute) >> 2; - if (isrest) { - music.rest(currentDuration * beat) + if (melody.currentNote === REST) { + music.rest(melody.currentDuration * beat) } else { - let keyNumber = note + (12 * (currentOctave - 1)); - let frequency = freqs.getNumber(NumberFormat.UInt16LE, keyNumber * 2) || 0; - music.playTone(frequency, currentDuration * beat); + music.playTone(getFrequencyForNote(melody.currentOctave, melody.currentNote), melody.currentDuration * beat); + } + + control.raiseEvent(MICROBIT_MELODY_ID, melody.background | MusicEvent.MelodyNotePlayed); + + if (melody.repeating && !melody.hasNextNote()) { + melody.reset(); + control.raiseEvent(MICROBIT_MELODY_ID, melody.background | MusicEvent.MelodyRepeated); } - melody.currentDuration = currentDuration; - melody.currentOctave = currentOctave; - const repeating = melody.repeating && currentPos == melody.melodyArray.length - 1; - melody.currentPos = repeating ? 0 : currentPos + 1; - - control.raiseEvent(MICROBIT_MELODY_ID, melody.background ? MusicEvent.BackgroundMelodyNotePlayed : MusicEvent.MelodyNotePlayed); - if (repeating) - control.raiseEvent(MICROBIT_MELODY_ID, melody.background ? MusicEvent.BackgroundMelodyRepeated : MusicEvent.MelodyRepeated); } - class Melody { - public melodyArray: string[]; - public currentDuration: number; - public currentOctave: number; - public currentPos: number; - public repeating: boolean; - public background: boolean; - - constructor(melodyArray: string[], options: MelodyOptions) { - this.melodyArray = melodyArray; - this.repeating = ((options & MelodyOptions.Forever) != 0); - this.repeating = this.repeating ? true : ((options & MelodyOptions.ForeverInBackground) != 0) - this.background = ((options & MelodyOptions.OnceInBackground) != 0); - this.background = this.background ? true : ((options & MelodyOptions.ForeverInBackground) != 0); - this.currentDuration = 4; //Default duration (Crotchet) - this.currentOctave = 4; //Middle octave - this.currentPos = 0; + export class MelodyReader { + currentOctave: number; + currentNote: number; + currentDuration: number; + options: MelodyOptions; + + constructor() { + this.reset(); } - hasNextNote() { - return this.repeating || this.currentPos < this.melodyArray.length; + get background() { + return (this.options & (MelodyOptions.OnceInBackground | MelodyOptions.ForeverInBackground)) ? 0xf0 : 0; } - nextNote(): string { - const currentNote = this.melodyArray[this.currentPos]; - return currentNote; + get repeating() { + return !!(this.options & (MelodyOptions.Forever | MelodyOptions.ForeverInBackground)); } + + setOptions(options: MelodyOptions) { + this.options = options; + } + + reset() { + this.currentOctave = 4; + this.currentNote = 0; + this.currentDuration = 4; + } + + readNote() { } + + hasNextNote(): boolean { + return false; + } + } + + /** + * This is an array that maps letter index to note index in an octave + * The note index is the number of semitones above C, so C=0, D=2, ..., B=11 + * + * There are no sharps in this array, only natural notes + */ + //% whenUsed + const offsetLookup = hex`090b0002040507`; + + //% whenUsed + const REST = -0xffff; + + export class MelodyStringReader extends MelodyReader { + melodyStringIndex: number; + constructor(public melody: string, public resetOctave: boolean) { + super(); + } + + reset() { + super.reset(); + this.melodyStringIndex = 0; + } + + readNote() { + this.eatWhitespace(); + if (this.resetOctave) { + this.currentOctave = 4; + } + + let note: number = undefined; + let modifier = 0; + + while (this.melodyStringIndex < this.melody.length) { + const c = this.melody.charCodeAt(this.melodyStringIndex++); + if (c == 32 /* space */) { + break; + } + + else if (c === 35 /* # */) { + modifier++; + } + else if (c === 45 /* - */ || c === 114 /* r */ || c === 82 /* R */) { + if (note !== undefined) { + this.melodyStringIndex--; + break; + } + note = -1; + } + else if (c === 98 /* b */) { + if (note === undefined) { + note = 11; + } + else { + modifier--; + } + } + else if (c >= 48 && c <= 57) { + // number + if (note === undefined) { + // invalid if we haven't seen a note yet, ignore the number + continue; + } + else { + this.melodyStringIndex--; + this.currentOctave = this.readNumber(); + } + } + else if (c >= 65 && c <= 71) { + // A-G + if (note !== undefined) { + this.melodyStringIndex--; + break; + } + note = offsetLookup[c - 65]; + } + else if (c >= 97 && c <= 103) { + // a-g + if (note !== undefined) { + this.melodyStringIndex--; + break; + } + note = offsetLookup[c - 97]; + } + else if (c === 58 /* : */) { + this.currentDuration = Math.max(1, this.readNumber()); + break; + } + } + + if (note === undefined || note < 0) { + // invalid note, treat as rest + this.currentNote = REST; + } + else { + // for whatever reason, we index our notes starting at 1 instead of 0, so add 1 to the note value + this.currentNote = note + modifier + 1; + } + + this.eatWhitespace(); + } + + readNumber() { + let result = 0; + while (this.melodyStringIndex < this.melody.length) { + const c = this.melody.charCodeAt(this.melodyStringIndex); + if (c < 48 || c > 57) break; + result = result * 10 + (c - 48); + this.melodyStringIndex++; + } + return result; + } + + eatWhitespace() { + while (this.melodyStringIndex < this.melody.length && this.melody.charAt(this.melodyStringIndex) == " ") { + this.melodyStringIndex++; + } + } + + hasNextNote(): boolean { + this.eatWhitespace(); + return this.melodyStringIndex < this.melody.length; + } + } + + export class MelodyArrayReader extends MelodyStringReader { + protected melodyArrayIndex: number; + constructor(private melodyArray: string[]) { + super(melodyArray[0], false); + this.melodyArrayIndex = 0; + } + + reset() { + super.reset(); + this.melodyArrayIndex = 0; + } + + readNote() { + this.melodyStringIndex = 0; + this.melody = this.melodyArray[this.melodyArrayIndex]; + super.readNote(); + + // we treat each array entry as a single note, ignore anything after the first note + this.melodyArrayIndex++; + } + + hasNextNote(): boolean { + return this.melodyArrayIndex < this.melodyArray.length; + } + } + + export class MelodyBufferReader extends MelodyReader { + position: number; + constructor(private melody: Buffer) { + super(); + this.position = 0; + } + + readNote() { + const noteNumber = this.melody[this.position]; + this.currentDuration = this.melody[this.position + 1]; + + if (noteNumber === 0) { + this.currentNote = REST; + } + else { + this.currentNote = (noteNumber % 12) + 1; + this.currentOctave = Math.idiv((noteNumber - 24), 12); + } + this.position += 2; + } + + hasNextNote(): boolean { + return this.position < this.melody.length; + } + } + + export function _startMelodyInternal(reader: MelodyReader, options: MelodyOptions) { + init(); + const isBackground = options & (MelodyOptions.OnceInBackground | MelodyOptions.ForeverInBackground); + reader.setOptions(options); + if (currentMelody != undefined) { + if (!isBackground && currentMelody.background) { + currentBackgroundMelody = currentMelody; + currentMelody = null; + control.raiseEvent(MICROBIT_MELODY_ID, MusicEvent.BackgroundMelodyPaused); + } + if (currentMelody) + control.raiseEvent(MICROBIT_MELODY_ID, currentMelody.background | MusicEvent.MelodyEnded); + currentMelody = reader; + control.raiseEvent(MICROBIT_MELODY_ID, currentMelody.background | MusicEvent.MelodyStarted); + } else { + currentMelody = reader; + control.raiseEvent(MICROBIT_MELODY_ID, currentMelody.background | MusicEvent.MelodyStarted); + // Only start the fiber once + control.inBackground(() => { + while (currentMelody.hasNextNote()) { + playNextNote(currentMelody); + if (!currentMelody.hasNextNote() && currentBackgroundMelody) { + // Swap the background melody back + currentMelody = currentBackgroundMelody; + currentBackgroundMelody = null; + control.raiseEvent(MICROBIT_MELODY_ID, MusicEvent.MelodyEnded); + control.raiseEvent(MICROBIT_MELODY_ID, MusicEvent.BackgroundMelodyResumed); + control.raiseEvent(MICROBIT_MELODY_ID, INTERNAL_MELODY_ENDED); + } + } + control.raiseEvent(MICROBIT_MELODY_ID, currentMelody.background | MusicEvent.MelodyEnded); + if (!currentMelody.background) + control.raiseEvent(MICROBIT_MELODY_ID, INTERNAL_MELODY_ENDED); + currentMelody = null; + }); + } + } + + // deprecated use StringArrayPlayable instead + export function _bufferToMelody(melody: Buffer) { + if (!melody) return []; + + let currentDuration = 4; + let currentOctave = -1; + const out: string[] = []; + + const notes = "c#d#ef#g#a#b" + let current = ""; + + // The buffer format is 2 bytes per note. First note byte is midi + // note number, second byte is duration in quarter beats. The note + // number 0 is reserved for rests + for (let i = 0; i < melody.length; i += 2) { + let octave = 4; + const note = melody[i] % 12; + if (melody[i] === 0) { + current = "r" + } + else { + current = notes.charAt(note); + if (current === "#") current = notes.charAt(note - 1) + current + + octave = Math.idiv((melody[i] - 24), 12) + } + + const duration = melody[i + 1]; + + if (octave !== currentOctave) { + current += octave + currentOctave = octave; + } + + if (duration !== currentDuration) { + current += ":" + duration; + currentDuration = duration; + } + + out.push(current); + } + + return out; } } diff --git a/libs/core/pins.cpp b/libs/core/pins.cpp index 965bbb61d40..1d2bfe360aa 100644 --- a/libs/core/pins.cpp +++ b/libs/core/pins.cpp @@ -3,67 +3,94 @@ #if MICROBIT_CODAL #include "Pin.h" #define PinCompat codal::Pin +#undef Button // need to get codal Button back in scope here +#include "MicroBitButton.h" // this include is missing in MicroBit.h from codal-microbit-v2 when DEVICE_BLE=0 #else #define PinCompat MicroBitPin #endif enum class DigitalPin { + //% blockIdentity="pins._digitalPin" P0 = MICROBIT_ID_IO_P0, + //% blockIdentity="pins._digitalPin" P1 = MICROBIT_ID_IO_P1, + //% blockIdentity="pins._digitalPin" P2 = MICROBIT_ID_IO_P2, + //% blockIdentity="pins._digitalPin" P3 = MICROBIT_ID_IO_P3, + //% blockIdentity="pins._digitalPin" P4 = MICROBIT_ID_IO_P4, + //% blockIdentity="pins._digitalPin" P5 = MICROBIT_ID_IO_P5, + //% blockIdentity="pins._digitalPin" P6 = MICROBIT_ID_IO_P6, + //% blockIdentity="pins._digitalPin" P7 = MICROBIT_ID_IO_P7, + //% blockIdentity="pins._digitalPin" P8 = MICROBIT_ID_IO_P8, + //% blockIdentity="pins._digitalPin" P9 = MICROBIT_ID_IO_P9, + //% blockIdentity="pins._digitalPin" P10 = MICROBIT_ID_IO_P10, + //% blockIdentity="pins._digitalPin" P11 = MICROBIT_ID_IO_P11, + //% blockIdentity="pins._digitalPin" P12 = MICROBIT_ID_IO_P12, + //% blockIdentity="pins._digitalPin" P13 = MICROBIT_ID_IO_P13, + //% blockIdentity="pins._digitalPin" P14 = MICROBIT_ID_IO_P14, + //% blockIdentity="pins._digitalPin" P15 = MICROBIT_ID_IO_P15, + //% blockIdentity="pins._digitalPin" P16 = MICROBIT_ID_IO_P16, + //% blockIdentity="pins._digitalPin" //% blockHidden=1 P19 = MICROBIT_ID_IO_P19, + //% blockIdentity="pins._digitalPin" //% blockHidden=1 P20 = MICROBIT_ID_IO_P20, }; enum class AnalogPin { + //% blockIdentity="pins._analogPin" P0 = MICROBIT_ID_IO_P0, + //% blockIdentity="pins._analogPin" P1 = MICROBIT_ID_IO_P1, + //% blockIdentity="pins._analogPin" P2 = MICROBIT_ID_IO_P2, + //% blockIdentity="pins._analogPin" P3 = MICROBIT_ID_IO_P3, + //% blockIdentity="pins._analogPin" P4 = MICROBIT_ID_IO_P4, - P10 = MICROBIT_ID_IO_P10, - //% block="P5 (write only)" + //% blockIdentity="pins._analogPin" P5 = MICROBIT_ID_IO_P5, - //% block="P6 (write only)" + //% blockIdentity="pins._analogPin" P6 = MICROBIT_ID_IO_P6, - //% block="P7 (write only)" + //% blockIdentity="pins._analogPin" P7 = MICROBIT_ID_IO_P7, - //% block="P8 (write only)" + //% blockIdentity="pins._analogPin" P8 = MICROBIT_ID_IO_P8, - //% block="P9 (write only)" + //% blockIdentity="pins._analogPin" P9 = MICROBIT_ID_IO_P9, - //% block="P11 (write only)" + //% blockIdentity="pins._analogPin" + P10 = MICROBIT_ID_IO_P10, + //% blockIdentity="pins._analogPin" P11 = MICROBIT_ID_IO_P11, - //% block="P12 (write only)" + //% blockIdentity="pins._analogPin" P12 = MICROBIT_ID_IO_P12, - //% block="P13 (write only)" + //% blockIdentity="pins._analogPin" P13 = MICROBIT_ID_IO_P13, - //% block="P14 (write only)" + //% blockIdentity="pins._analogPin" P14 = MICROBIT_ID_IO_P14, - //% block="P15 (write only)" + //% blockIdentity="pins._analogPin" P15 = MICROBIT_ID_IO_P15, - //% block="P16 (write only)" + //% blockIdentity="pins._analogPin" P16 = MICROBIT_ID_IO_P16, - //% block="P19 (write only)" + //% blockIdentity="pins._analogPin" //% blockHidden=1 P19 = MICROBIT_ID_IO_P19, - //% block="P20 (write only)" + //% blockIdentity="pins._analogPin" //% blockHidden=1 P20 = MICROBIT_ID_IO_P20 }; @@ -152,9 +179,9 @@ namespace pins { */ //% help=pins/digital-read-pin weight=30 //% blockId=device_get_digital_pin block="digital read|pin %name" blockGap=8 - //% name.fieldEditor="gridpicker" name.fieldOptions.columns=4 - //% name.fieldOptions.tooltips="false" name.fieldOptions.width="250" - int digitalReadPin(DigitalPin name) { + //% name.label="value" + //% name.shadow=digital_pin_shadow + int digitalReadPin(int name) { PINREAD(getDigitalValue()); } @@ -165,10 +192,10 @@ namespace pins { */ //% help=pins/digital-write-pin weight=29 //% blockId=device_set_digital_pin block="digital write|pin %name|to %value" + //% name.label="pin" value.label="value" //% value.min=0 value.max=1 - //% name.fieldEditor="gridpicker" name.fieldOptions.columns=4 - //% name.fieldOptions.tooltips="false" name.fieldOptions.width="250" - void digitalWritePin(DigitalPin name, int value) { + //% name.shadow=digital_pin_shadow + void digitalWritePin(int name, int value) { PINOP(setDigitalValue(value)); } @@ -178,9 +205,9 @@ namespace pins { */ //% help=pins/analog-read-pin weight=25 //% blockId=device_get_analog_pin block="analog read|pin %name" blockGap="8" - //% name.fieldEditor="gridpicker" name.fieldOptions.columns=4 - //% name.fieldOptions.tooltips="false" name.fieldOptions.width="250" - int analogReadPin(AnalogPin name) { + //% name.label="value" + //% name.shadow=analog_read_write_pin_shadow + int analogReadPin(int name) { PINREAD(getAnalogValue()); } @@ -191,10 +218,10 @@ namespace pins { */ //% help=pins/analog-write-pin weight=24 //% blockId=device_set_analog_pin block="analog write|pin %name|to %value" blockGap=8 + //% name.label="pin" value.label="value" //% value.min=0 value.max=1023 - //% name.fieldEditor="gridpicker" name.fieldOptions.columns=4 - //% name.fieldOptions.tooltips="false" name.fieldOptions.width="250" - void analogWritePin(AnalogPin name, int value) { + //% name.shadow=analog_pin_shadow + void analogWritePin(int name, int value) { PINOP(setAnalogValue(value)); } @@ -202,13 +229,13 @@ namespace pins { * Configure the pulse-width modulation (PWM) period of the analog output in microseconds. * If this pin is not configured as an analog output (using `analog write pin`), the operation has no effect. * @param name analog pin to set period to, eg: AnalogPin.P0 - * @param micros period in micro seconds. eg:20000 + * @param micros period in microseconds. eg:20000 */ //% help=pins/analog-set-period weight=23 blockGap=8 //% blockId=device_set_analog_period block="analog set period|pin %pin|to (Âĩs)%micros" - //% pin.fieldEditor="gridpicker" pin.fieldOptions.columns=4 - //% pin.fieldOptions.tooltips="false" - void analogSetPeriod(AnalogPin name, int micros) { + //% name.label="pin" micros.label="microseconds" + //% pin.shadow=analog_pin_shadow + void analogSetPeriod(int name, int micros) { PINOP(setAnalogPeriodUs(micros)); } @@ -217,10 +244,13 @@ namespace pins { * @param name digital pin to register to, eg: DigitalPin.P0 * @param pulse the value of the pulse, eg: PulseValue.High */ - //% help=pins/on-pulsed weight=22 blockGap=16 advanced=true + //% help=pins/on-pulsed advanced=true //% blockId=pins_on_pulsed block="on|pin %pin|pulsed %pulse" //% pin.fieldEditor="gridpicker" pin.fieldOptions.columns=4 //% pin.fieldOptions.tooltips="false" pin.fieldOptions.width="250" + //% group="Pulse" + //% weight=25 + //% blockGap=8 void onPulsed(DigitalPin name, PulseValue pulse, Action body) { MicroBitPin* pin = getPin((int)name); if (!pin) return; @@ -234,7 +264,9 @@ namespace pins { */ //% help=pins/pulse-duration advanced=true //% blockId=pins_pulse_duration block="pulse duration (Âĩs)" - //% weight=21 blockGap=8 + //% group="Pulse" + //% weight=24 + //% blockGap=8 int pulseDuration() { return pxt::lastEvent.timestamp; } @@ -246,11 +278,14 @@ namespace pins { * @param maximum duration in microseconds */ //% blockId="pins_pulse_in" block="pulse in (Âĩs)|pin %name|pulsed %value" - //% weight=20 advanced=true + //% name.label="pin" + //% advanced=true //% help=pins/pulse-in - //% name.fieldEditor="gridpicker" name.fieldOptions.columns=4 - //% name.fieldOptions.tooltips="false" name.fieldOptions.width="250" - int pulseIn(DigitalPin name, PulseValue value, int maxDuration = 2000000) { + //% name.shadow=digital_pin_shadow + //% group="Pulse" + //% weight=23 + //% blockGap=8 + int pulseIn(int name, PulseValue value, int maxDuration = 2000000) { MicroBitPin* pin = getPin((int)name); if (!pin) return 0; @@ -290,11 +325,12 @@ namespace pins { */ //% help=pins/servo-write-pin weight=20 //% blockId=device_set_servo_pin block="servo write|pin %name|to %value" blockGap=8 + //% name.label="pin" value.label="angle" //% parts=microservo trackArgs=0 //% value.min=0 value.max=180 - //% name.fieldEditor="gridpicker" name.fieldOptions.columns=4 - //% name.fieldOptions.tooltips="false" name.fieldOptions.width="250" - void servoWritePin(AnalogPin name, int value) { + //% name.shadow=analog_pin_shadow + //% group="Servo" + void servoWritePin(int name, int value) { PINOP(setServoValue(value)); } @@ -302,20 +338,21 @@ namespace pins { * Specifies that a continuous servo is connected. */ //% - void servoSetContinuous(AnalogPin name, bool value) { + void servoSetContinuous(int name, bool value) { // handled in simulator } /** * Configure the IO pin as an analog/pwm output and set a pulse width. The period is 20 ms period and the pulse width is set based on the value given in **microseconds** or `1/1000` milliseconds. * @param name pin name - * @param micros pulse duration in micro seconds, eg:1500 + * @param micros pulse duration in microseconds, eg:1500 */ //% help=pins/servo-set-pulse weight=19 //% blockId=device_set_servo_pulse block="servo set pulse|pin %value|to (Âĩs) %micros" - //% value.fieldEditor="gridpicker" value.fieldOptions.columns=4 - //% value.fieldOptions.tooltips="false" value.fieldOptions.width="250" - void servoSetPulse(AnalogPin name, int micros) { + //% name.label="pin" micros.label="microseconds" + //% value.shadow=analog_pin_shadow + //% group="Servo" + void servoSetPulse(int name, int micros) { PINOP(setServoPulseUs(micros)); } @@ -323,16 +360,20 @@ namespace pins { PinCompat* pitchPin = NULL; uint8_t pitchVolume = 0xff; bool analogTonePlaying = false; + bool edgeConnectorSoundDisabled = false; /** * Set the pin used when using analog pitch or music. * @param name pin to modulate pitch from */ //% blockId=device_analog_set_pitch_pin block="analog set pitch pin %name" - //% help=pins/analog-set-pitch-pin weight=3 advanced=true - //% name.fieldEditor="gridpicker" name.fieldOptions.columns=4 - //% name.fieldOptions.tooltips="false" name.fieldOptions.width="250" - void analogSetPitchPin(AnalogPin name) { + //% name.label="value" + //% help=pins/analog-set-pitch-pin advanced=true + //% name.shadow=analog_pin_shadow + //% group="Pins" + //% weight=12 + //% blockGap=8 + void analogSetPitchPin(int name) { pitchPin = getPin((int)name); } @@ -351,6 +392,7 @@ namespace pins { * @param volume the intensity of the sound from 0..255 */ //% blockId=device_analog_set_pitch_volume block="analog set pitch volume $volume" + //% volume.label="value" //% help=pins/analog-set-pitch-volume weight=3 advanced=true //% volume.min=0 volume.max=255 //% deprecated @@ -359,7 +401,7 @@ namespace pins { if (analogTonePlaying) { int v = pitchVolume == 0 ? 0 : 1 << (pitchVolume >> 5); - if (NULL != pitchPin) + if (NULL != pitchPin && !edgeConnectorSoundDisabled) pitchPin->setAnalogValue(v); } } @@ -375,12 +417,16 @@ namespace pins { } /** - * Emit a plse-width modulation (PWM) signal to the current pitch pin. Use `analog set pitch pin` to define the pitch pin. + * Send a pulse-width modulation (PWM) signal to the current pitch pin. Use `analog set pitch pin` to define the pitch pin. * @param frequency frequency to modulate in Hz. - * @param ms duration of the pitch in milli seconds. + * @param ms duration of the pitch in milliseconds. */ //% blockId=device_analog_pitch block="analog pitch %frequency|for (ms) %ms" - //% help=pins/analog-pitch weight=4 async advanced=true blockGap=8 + //% frequency.label="frequency" ms.label="duration" + //% help=pins/analog-pitch async advanced=true + //% group="Pins" + //% weight=14 + //% blockGap=8 void analogPitch(int frequency, int ms) { // init pins if needed if (NULL == pitchPin) { @@ -388,10 +434,12 @@ namespace pins { pitchPin = &uBit.audio.virtualOutputPin; #else pitchPin = getPin((int)AnalogPin::P0); -#endif +#endif } // set pitch analogTonePlaying = true; + +#if MICROBIT_CODAL if (NULL != pitchPin) pinAnalogSetPitch(pitchPin, frequency, ms); // clear pitch @@ -403,19 +451,35 @@ namespace pins { // causes issues with v2 DMA. // fiber_sleep(5); } +#else + if (NULL != pitchPin && !edgeConnectorSoundDisabled) + pinAnalogSetPitch(pitchPin, frequency, ms); + // clear pitch + if (ms > 0) { + fiber_sleep(ms); + if (NULL != pitchPin && !edgeConnectorSoundDisabled) + pitchPin->setAnalogValue(0); + analogTonePlaying = false; + // causes issues with v2 DMA. + // fiber_sleep(5); + } +#endif } /** - * Configure the pull directiion of of a pin. + * Configure the pull direction of of a pin. * @param name pin to set the pull mode on, eg: DigitalPin.P0 * @param pull one of the mbed pull configurations, eg: PinPullMode.PullUp */ - //% help=pins/set-pull weight=3 advanced=true + //% help=pins/set-pull advanced=true //% blockId=device_set_pull block="set pull|pin %pin|to %pull" - //% pin.fieldEditor="gridpicker" pin.fieldOptions.columns=4 - //% pin.fieldOptions.tooltips="false" pin.fieldOptions.width="250" - void setPull(DigitalPin name, PinPullMode pull) { + //% name.label="pin" + //% pin.shadow=digital_pin_shadow + //% group="Pins" + //% weight=15 + //% blockGap=8 + void setPull(int name, PinPullMode pull) { #if MICROBIT_CODAL codal::PullMode m = pull == PinPullMode::PullDown ? codal::PullMode::Down @@ -437,11 +501,14 @@ namespace pins { * @param name pin to set the event mode on, eg: DigitalPin.P0 * @param type the type of events for this pin to emit, eg: PinEventType.Edge */ - //% help=pins/set-events weight=4 advanced=true + //% help=pins/set-events advanced=true //% blockId=device_set_pin_events block="set pin %pin|to emit %type|events" - //% pin.fieldEditor="gridpicker" pin.fieldOptions.columns=4 - //% pin.fieldOptions.tooltips="false" pin.fieldOptions.width="250" - void setEvents(DigitalPin name, PinEventType type) { + //% name.label="pin" + //% pin.shadow=digital_pin_shadow + //% group="Pins" + //% weight=13 + //% blockGap=8 + void setEvents(int name, PinEventType type) { getPin((int)name)->eventOn((int)type); } @@ -462,12 +529,15 @@ namespace pins { * @param name pin of Neopixel strip, eg: DigitalPin.P1 * @param value width of matrix (at least ``2``) */ - //% help=pins/neopixel-matrix-width weight=3 advanced=true - //% blockId=pin_neopixel_matrix_width block="neopixel matrix width|pin %pin %width" blockGap=8 - //% pin.fieldEditor="gridpicker" pin.fieldOptions.columns=4 - //% pin.fieldOptions.tooltips="false" pin.fieldOptions.width="250" + //% help=pins/neopixel-matrix-width advanced=true + //% blockId=pin_neopixel_matrix_width block="neopixel matrix width|pin %pin %width" + //% pin.label="pin" width.label="width" + //% pin.shadow=digital_pin_shadow //% width.defl=5 width.min=2 - void setMatrixWidth(DigitalPin pin, int width) {} + //% group="Pins" + //% weight=11 + //% blockGap=8 + void setMatrixWidth(int pin, int width) {} #if MICROBIT_CODAL #define BUFFER_TYPE uint8_t* @@ -506,8 +576,12 @@ namespace pins { * Write to the SPI slave and return the response * @param value Data to be sent to the SPI slave */ - //% help=pins/spi-write weight=5 advanced=true + //% help=pins/spi-write advanced=true //% blockId=spi_write block="spi write %value" + //% value.label="value" + //% group="SPI" + //% blockGap=8 + //% weight=53 int spiWrite(int value) { auto p = allocSPI(); return p->write(value); @@ -541,8 +615,12 @@ namespace pins { * Set the SPI frequency * @param frequency the clock frequency, eg: 1000000 */ - //% help=pins/spi-frequency weight=4 advanced=true + //% help=pins/spi-frequency advanced=true //% blockId=spi_frequency block="spi frequency %frequency" + //% frequency.label="value" + //% group="SPI" + //% blockGap=8 + //% weight=55 void spiFrequency(int frequency) { auto p = allocSPI(); p->frequency(frequency); @@ -553,8 +631,12 @@ namespace pins { * @param bits the number of bits, eg: 8 * @param mode the mode, eg: 3 */ - //% help=pins/spi-format weight=3 advanced=true + //% help=pins/spi-format advanced=true //% blockId=spi_format block="spi format|bits %bits|mode %mode" + //% bits.label="bits" mode.label="mode" + //% group="SPI" + //% blockGap=8 + //% weight=54 void spiFormat(int bits, int mode) { auto p = allocSPI(); p->format(bits, mode); @@ -570,15 +652,16 @@ namespace pins { * Set the MOSI, MISO, SCK pins used by the SPI connection * */ - //% help=pins/spi-pins weight=2 advanced=true + //% help=pins/spi-pins advanced=true //% blockId=spi_pins block="spi set pins|MOSI %mosi|MISO %miso|SCK %sck" - //% mosi.fieldEditor="gridpicker" mosi.fieldOptions.columns=4 - //% mosi.fieldOptions.tooltips="false" mosi.fieldOptions.width="250" - //% miso.fieldEditor="gridpicker" miso.fieldOptions.columns=4 - //% miso.fieldOptions.tooltips="false" miso.fieldOptions.width="250" - //% sck.fieldEditor="gridpicker" sck.fieldOptions.columns=4 - //% sck.fieldOptions.tooltips="false" sck.fieldOptions.width="250" - void spiPins(DigitalPin mosi, DigitalPin miso, DigitalPin sck) { + //% mosi.label="MOSI pin" miso.label="MISO pin" sck.label="SCK pin" + //% mosi.shadow=digital_pin_shadow + //% miso.shadow=digital_pin_shadow + //% sck.shadow=digital_pin_shadow + //% group="SPI" + //% blockGap=8 + //% weight=51 + void spiPins(int mosi, int miso, int sck) { if (NULL != spi) { delete spi; spi = NULL; @@ -590,7 +673,7 @@ namespace pins { * Mounts a push button on the given pin */ //% help=pins/push-button advanced=true - void pushButton(DigitalPin pin) { + void pushButton(int pin) { new MicroBitButton((PinName)getPin((int)(pin))->name, (int)pin, MICROBIT_BUTTON_ALL_EVENTS, PinMode::PullUp); } @@ -599,17 +682,33 @@ namespace pins { * @param name pin to modulate pitch from */ //% blockId=pin_set_audio_pin block="set audio pin $name" - //% help=pins/set-audio-pin weight=3 - //% name.fieldEditor="gridpicker" name.fieldOptions.columns=4 - //% name.fieldOptions.tooltips="false" name.fieldOptions.width="250" + //% name.label="value" + //% help=pins/set-audio-pin + //% name.shadow=digital_pin_shadow //% weight=1 - void setAudioPin(AnalogPin name) { + //% blockGap=8 + void setAudioPin(int name) { #if MICROBIT_CODAL uBit.audio.setPin(*getPin((int)name)); - uBit.audio.setPinEnabled(true); + uBit.audio.setPinEnabled(!edgeConnectorSoundDisabled); #else // v1 behavior pins::analogSetPitchPin(name); #endif } -} \ No newline at end of file + + /** + * Sets whether or not audio will be output using a pin on the edge + * connector. + */ + //% blockId=pin_set_audio_pin_enabled + //% block="set audio pin enabled $enabled" + //% enabled.label="value" + //% weight=0 help=pins/set-audio-pin-enabled + void setAudioPinEnabled(bool enabled) { + edgeConnectorSoundDisabled = !enabled; +#if MICROBIT_CODAL + uBit.audio.setPinEnabled(enabled); +#endif + } +} diff --git a/libs/core/pins.ts b/libs/core/pins.ts index c75b86c95f9..f44c5607ce1 100644 --- a/libs/core/pins.ts +++ b/libs/core/pins.ts @@ -3,7 +3,91 @@ */ //% color=#B22222 weight=30 icon="\uf140" //% advanced=true +//% groups='["Pins", "Pulse", "I2C", "SPI", "micro:bit (V2)"]' namespace pins { + /** + * Returns the value of a C++ runtime constant + */ + //% help=pins/digital-pin + //% shim=TD_ID + //% blockId=digital_pin + //% block="digital pin $pin" + //% pin.fieldEditor=pinpicker + //% pin.fieldOptions.columns=4 + //% pin.fieldOptions.tooltips="false" + //% group="Pins" + //% weight=17 + //% blockGap=8 + //% advanced=true + //% decompilerShadowAlias=digital_pin_shadow + export function _digitalPin(pin: DigitalPin): number { + return pin; + } + + /** + * Returns the value of a C++ runtime constant + */ + //% help=pins/analog-pin + //% shim=TD_ID + //% blockId=analog_pin + //% block="analog pin $pin" + //% pin.fieldEditor=pinpicker + //% pin.fieldOptions.columns=4 + //% pin.fieldOptions.tooltips="false" + //% group="Pins" + //% weight=16 + //% blockGap=8 + //% advanced=true + //% decompilerShadowAlias=analog_pin_shadow + export function _analogPin(pin: AnalogPin): number { + return pin; + } + + /** + * Returns the value of a C++ runtime constant + */ + //% help=pins/digital-pin + //% shim=TD_ID + //% blockId=digital_pin_shadow + //% block="$pin" + //% pin.fieldEditor=pinpicker + //% pin.fieldOptions.columns=4 + //% pin.fieldOptions.tooltips="false" + //% blockHidden=1 + export function _digitalPinShadow(pin: DigitalPin): number { + return pin; + } + + /** + * Returns the value of a C++ runtime constant + */ + //% help=pins/analog-pin + //% shim=TD_ID + //% blockId=analog_pin_shadow + //% block="$pin" + //% pin.fieldEditor=pinpicker + //% pin.fieldOptions.columns=4 + //% pin.fieldOptions.tooltips="false" + //% blockHidden=1 + export function _analogPinShadow(pin: AnalogPin): number { + return pin; + } + + /** + * Returns the value of a C++ runtime constant + */ + //% help=pins/analog-pin + //% shim=TD_ID + //% blockId=analog_read_write_pin_shadow + //% block="$pin" + //% pin.fieldEditor=pinpicker + //% pin.fieldOptions.columns=4 + //% pin.fieldOptions.tooltips="false" + //% blockHidden=1 + export function _analogReadWritePinShadow(pin: AnalogReadWritePin): number { + return pin; + } + /** * Map 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. * @param value value to map in ranges @@ -14,6 +98,7 @@ namespace pins { */ //% help=pins/map weight=23 //% blockId=pin_map block="map %value|from low %fromLow|from high %fromHigh|to low %toLow|to high %toHigh" + //% value.label="value" fromLow.label="from low" fromHigh.label="from high" toLow.label="to low" toHigh.label="to high" export function map(value: number, fromLow: number, fromHigh: number, toLow: number, toHigh: number): number { return ((value - fromLow) * (toHigh - toLow)) / (fromHigh - fromLow) + toLow; } @@ -23,6 +108,9 @@ namespace pins { */ //% help=pins/i2c-read-number blockGap=8 advanced=true //% blockId=pins_i2c_readnumber block="i2c read number|at address %address|of format %format|repeated %repeat" weight=7 + //% address.label="address" repeated.label="repeated" + //% group="I2C" + //% weight=45 export function i2cReadNumber(address: number, format: NumberFormat, repeated?: boolean): number { let buf = pins.i2cReadBuffer(address, pins.sizeOf(format), repeated) return buf.getNumber(format, 0) @@ -33,6 +121,9 @@ namespace pins { */ //% help=pins/i2c-write-number blockGap=8 advanced=true //% blockId=i2c_writenumber block="i2c write number|at address %address|with value %value|of format %format|repeated %repeat" weight=6 + //% address.label="address" value.label="value" repeated.label="repeated" + //% group="I2C" + //% weight=44 export function i2cWriteNumber(address: number, value: number, format: NumberFormat, repeated?: boolean): void { let buf = createBuffer(pins.sizeOf(format)) buf.setNumber(format, 0, value) diff --git a/libs/core/pinscompat.ts b/libs/core/pinscompat.ts index edc9b91e6e9..a6a07b80031 100644 --- a/libs/core/pinscompat.ts +++ b/libs/core/pinscompat.ts @@ -9,6 +9,21 @@ enum PinEvent { Fall = DAL.MICROBIT_PIN_EVT_FALL, // DEVICE_PIN_EVT_FALL } +enum AnalogReadWritePin { + //% blockIdentity="pins._analogReadWritePinShadow" + P0 = AnalogPin.P0, + //% blockIdentity="pins._analogReadWritePinShadow" + P1 = AnalogPin.P1, + //% blockIdentity="pins._analogReadWritePinShadow" + P2 = AnalogPin.P2, + //% blockIdentity="pins._analogReadWritePinShadow" + P3 = AnalogPin.P3, + //% blockIdentity="pins._analogReadWritePinShadow" + P4 = AnalogPin.P4, + //% blockIdentity="pins._analogReadWritePinShadow" + P10 = AnalogPin.P10, +} + //% noRefCounting fixedInstances interface DigitalInOutPin { digitalRead(): boolean; diff --git a/libs/core/playable.ts b/libs/core/playable.ts new file mode 100644 index 00000000000..f55fab9e5e1 --- /dev/null +++ b/libs/core/playable.ts @@ -0,0 +1,169 @@ +namespace music { + export enum PlaybackMode { + //% block="until done" + UntilDone, + //% block="in background" + InBackground, + //% block="looping in background" + LoopingInBackground + } + + let looping: Playable[]; + + export class Playable { + stopped: boolean; + constructor() { + + } + + _play(playbackMode: PlaybackMode) { + // subclass + } + + loop() { + if (!looping) { + looping = []; + } + + looping.push(this); + this.stopped = false; + + control.runInParallel(() => { + while (!this.stopped) { + this._play(PlaybackMode.UntilDone); + } + }); + } + } + + export class StringArrayPlayable extends Playable { + protected reader: MelodyReader; + constructor(notes: string[] | string | Buffer, private tempo: number) { + super(); + + if (typeof notes === "string") { + this.reader = new MelodyStringReader(notes, true); + } + else if (Array.isArray(notes)) { + this.reader = new MelodyArrayReader(notes as string[]); + } + else { + this.reader = new MelodyBufferReader(notes as Buffer); + } + } + + _play(playbackMode: PlaybackMode) { + if (this.tempo) { + music.setTempo(this.tempo); + } + if (playbackMode == PlaybackMode.InBackground) { + _startMelodyInternal(this.reader, MelodyOptions.OnceInBackground); + } + else if (playbackMode == PlaybackMode.LoopingInBackground) { + _startMelodyInternal(this.reader, MelodyOptions.ForeverInBackground); + } + else { + _startMelodyInternal(this.reader, MelodyOptions.Once); + waitForMelodyEnd(); + } + } + } + + export class TonePlayable extends Playable { + constructor(public pitch: number, public duration: number) { + super(); + } + + _play(playbackMode: PlaybackMode) { + if (playbackMode === PlaybackMode.InBackground) { + control.runInParallel(() => music.playTone(this.pitch, this.duration)); + } + else if (playbackMode === PlaybackMode.UntilDone) { + music.playTone(this.pitch, this.duration); + } + else { + this.loop(); + } + } + } + + /** + * Play a song, melody, or other sound. The music plays until finished or can play as a + * background task. + * @param toPlay the song or melody to play + * @param playbackMode play the song or melody until it's finished or as background task + */ + //% blockId="music_playable_play" + //% block="play $toPlay $playbackMode" + //% toPlay.label="sound" + //% toPlay.shadow=music_string_playable + //% group="Melody" + //% help="music/play" + //% blockHidden + export function play(toPlay: Playable, playbackMode: PlaybackMode) { + toPlay._play(playbackMode); + } + + //% blockId="music_playable_play_default_bkg" + //% block="play $toPlay $playbackMode" + //% toPlay.label="sound" + //% toPlay.shadow=music_string_playable + //% playbackMode.defl=music.PlaybackMode.InBackground + //% group="Melody" + //% help="music/play" + //% blockHidden + export function _playDefaultBackground(toPlay: Playable, playbackMode: PlaybackMode) { + return play(toPlay, playbackMode); + } + + /** + * Play a melody from the melody editor + * @param melody string of up to eight notes [C D E F G A B C5] or rests [-] separated by spaces, which will be played one at a time, ex: "E D G F B A C5 B " + * @param bpm number in beats per minute dictating how long each note will play + */ + //% blockId="music_string_playable" + //% block="melody $melody at tempo $bpm|(bpm)" + //% melody.label="melody" bpm.label="tempo" + //% weight=85 blockGap=8 + //% help=music/string-playable + //% group="Melody" + //% toolboxParent=music_playable_play + //% toolboxParentArgument=toPlay + //% duplicateShadowOnDrag + //% melody.shadow=melody_editor + //% bpm.min=40 bpm.max=500 + //% bpm.defl=120 + export function stringPlayable(melody: string, bpm: number): Playable { + return new StringArrayPlayable(melody, bpm); + } + + /** + * Plays a tone through pin ``P0`` for the given duration. + * @param note pitch of the tone to play in Hertz (Hz). + * @param duration tone duration in milliseconds (ms) + */ + //% blockId="music_tone_playable" + //% block="tone $note for $duration" + //% note.label="note" duration.label="duration" + //% toolboxParent=music_playable_play + //% toolboxParentArgument=toPlay + //% group="Tone" + //% weight=85 + //% duplicateShadowOnDrag + //% note.shadow=device_note + //% duration.shadow=device_beat + //% parts="headphone" + //% help=music/tone-playable + export function tonePlayable(note: number, duration: number): Playable { + return new TonePlayable(note, duration); + } + + export function _stopPlayables() { + if (!looping) return; + + for (const p of looping) { + p.stopped = true; + } + looping = undefined; + } +} \ No newline at end of file diff --git a/libs/core/pxt.json b/libs/core/pxt.json index 00013ecd6c8..127d6422dcb 100644 --- a/libs/core/pxt.json +++ b/libs/core/pxt.json @@ -33,6 +33,8 @@ "gestures.jres", "control.ts", "control.cpp", + "controlgc.cpp", + "perfcounters.ts", "interval.ts", "gcstats.ts", "console.ts", @@ -61,16 +63,19 @@ "sendbufferbrightness.s", "light.cpp", "logo.cpp", + "loops.ts", "touchmode.cpp", "soundexpressions.ts", "soundexpressions.cpp", "parts/speaker.svg", - "parts/headphone.svg" + "parts/headphone.svg", + "playable.ts" ], "testFiles": [], "public": true, "dependencies": {}, "dalDTS": { + "corePackage": ".", "compileServiceVariant": "mbcodal", "includeDirs": [ "libraries/codal-core/inc", diff --git a/libs/core/pxtcore.h b/libs/core/pxtcore.h index 14af8637f86..f986ab240ca 100644 --- a/libs/core/pxtcore.h +++ b/libs/core/pxtcore.h @@ -14,14 +14,14 @@ void debuglog(const char *format, ...); #define xmalloc malloc #define xfree free -#define GC_MAX_ALLOC_SIZE 9000 - #define NON_GC_HEAP_RESERVATION 1024 #ifdef CODAL_CONFIG_H #define MICROBIT_CODAL 1 +#define GC_MAX_ALLOC_SIZE 11000 #else #define MICROBIT_CODAL 0 +#define GC_MAX_ALLOC_SIZE 9000 #define GC_BLOCK_SIZE 256 #endif diff --git a/libs/core/serial.cpp b/libs/core/serial.cpp index 65e328a473c..5307c0987f2 100644 --- a/libs/core/serial.cpp +++ b/libs/core/serial.cpp @@ -46,6 +46,10 @@ enum BaudRate { //% weight=2 color=#002050 icon="\uf287" //% advanced=true namespace serial { +#if MICROBIT_CODAL + bool is_redirected; +#endif + // note that at least one // followed by % is needed per declaration! /** @@ -54,6 +58,7 @@ namespace serial { */ //% help=serial/read-until //% blockId=serial_read_until block="serial|read until %delimiter=serial_delimiter_conv" + //% delimiter.label="delimiter" //% weight=19 String readUntil(String delimiter) { return PSTR(uBit.serial.readUntil(MSTR(delimiter))); @@ -77,6 +82,7 @@ namespace serial { */ //% help=serial/on-data-received //% weight=18 blockId=serial_on_data_received block="serial|on data received %delimiters=serial_delimiter_conv" + //% delimiters.label="delimiter" void onDataReceived(String delimiters, Action body) { uBit.serial.eventOn(MSTR(delimiters)); registerWithDal(MICROBIT_ID_SERIAL, MICROBIT_SERIAL_EVT_DELIM_MATCH, body); @@ -90,6 +96,7 @@ namespace serial { //% help=serial/write-string //% weight=87 blockGap=8 //% blockId=serial_writestring block="serial|write string %text" + //% text.label="value" //% text.shadowOptions.toString=true void writeString(String text) { if (!text) return; @@ -101,6 +108,7 @@ namespace serial { * Send a buffer through serial connection */ //% blockId=serial_writebuffer block="serial|write buffer %buffer=serial_readbuffer" + //% buffer.label="value" //% help=serial/write-buffer advanced=true weight=6 void writeBuffer(Buffer buffer) { if (!buffer) return; @@ -109,11 +117,12 @@ namespace serial { } /** - * Read multiple characters from the receive buffer. + * Read multiple characters from the receive buffer. * If length is positive, pauses until enough characters are present. * @param length default buffer length */ //% blockId=serial_readbuffer block="serial|read buffer %length" + //% length.label="value" //% help=serial/read-buffer advanced=true weight=5 Buffer readBuffer(int length) { auto mode = SYNC_SLEEP; @@ -140,8 +149,8 @@ namespace serial { case SerialPin::USB_TX: name = USBTX; return true; case SerialPin::USB_RX: name = USBRX; return true; #endif - default: - auto pin = getPin(p); + default: + auto pin = getPin(p); if (NULL != pin) { name = (PinName)pin->name; return true; @@ -167,8 +176,10 @@ namespace serial { //% blockGap=8 void redirect(SerialPin tx, SerialPin rx, BaudRate rate) { #if MICROBIT_CODAL - if (getPin(tx) && getPin(rx)) + if (getPin(tx) && getPin(rx)) { uBit.serial.redirect(*getPin(tx), *getPin(rx)); + is_redirected = 1; + } uBit.serial.setBaud(rate); #else PinName txn; @@ -179,6 +190,22 @@ namespace serial { #endif } + //% + int redirectWithStatus(SerialPin tx, SerialPin rx) { +#if MICROBIT_CODAL + if (getPin(tx) && getPin(rx)) { + is_redirected = 1; + return uBit.serial.redirect(*getPin(tx), *getPin(rx)); + } +#else + PinName txn; + PinName rxn; + if (tryResolvePin(tx, txn) && tryResolvePin(rx, rxn)) + return uBit.serial.redirect(txn, rxn); +#endif + return MICROBIT_INVALID_PARAMETER; + } + /** Set the baud rate of the serial port */ @@ -203,6 +230,7 @@ namespace serial { //% blockId=serial_redirect_to_usb block="serial|redirect to USB" void redirectToUSB() { #if MICROBIT_CODAL + is_redirected = false; uBit.serial.redirect(uBit.io.usbTx, uBit.io.usbRx); uBit.serial.setBaud(115200); #else @@ -217,6 +245,7 @@ namespace serial { */ //% help=serial/set-rx-buffer-size //% blockId=serialSetRxBufferSize block="serial set rx buffer size to $size" + //% size.label="value" //% advanced=true void setRxBufferSize(uint8_t size) { uBit.serial.setRxBufferSize(size); @@ -228,8 +257,32 @@ namespace serial { */ //% help=serial/set-tx-buffer-size //% blockId=serialSetTxBufferSize block="serial set tx buffer size to $size" + //% size.label="value" //% advanced=true void setTxBufferSize(uint8_t size) { uBit.serial.setTxBufferSize(size); } + + /** Send DMESG debug buffer over serial. */ + //% + void writeDmesg() { + pxt::dumpDmesg(); + } +} + +namespace pxt { + +static void sendString(const char *c, int len) { + while (len--) + uBit.serial.putc(*c++); +} + +void dumpDmesg() { +#if MICROBIT_CODAL + if (serial::is_redirected) + return; + microbit_dmesg_flush(); +#endif +} + } diff --git a/libs/core/serial.ts b/libs/core/serial.ts index 8121a5ca084..e425301d8bd 100644 --- a/libs/core/serial.ts +++ b/libs/core/serial.ts @@ -43,6 +43,7 @@ namespace serial { //% weight=90 //% help=serial/write-line blockGap=8 //% blockId=serial_writeline block="serial|write line %text" + //% text.label="value" //% text.shadowOptions.toString=true export function writeLine(text: string): void { if (!text) text = ""; @@ -65,6 +66,7 @@ namespace serial { //% weight=1 //% help=serial/set-write-line-padding //% blockId=serialWriteNewLinePadding block="serial set write line padding to $length" + //% length.label="value" //% advanced=true //% length.min=0 length.max=128 export function setWriteLinePadding(length: number) { @@ -77,6 +79,7 @@ namespace serial { //% help=serial/write-number //% weight=89 blockGap=8 //% blockId=serial_writenumber block="serial|write number %value" + //% value.label="value" export function writeNumber(value: number): void { writeString(value.toString()); } @@ -87,6 +90,7 @@ namespace serial { //% help=serial/write-numbers //% weight=86 //% blockId=serial_writenumbers block="serial|write numbers %values" + //% values.label="value" export function writeNumbers(values: number[]): void { if (!values) return; for (let i = 0; i < values.length; ++i) { @@ -104,6 +108,7 @@ namespace serial { //% weight=88 blockGap=8 //% help=serial/write-value //% blockId=serial_writevalue block="serial|write value %name|= %value" + //% name.label="name" value.label="value" export function writeValue(name: string, value: number): void { writeLine((name ? name + ":" : "") + value); } diff --git a/libs/core/shims.d.ts b/libs/core/shims.d.ts index 55fe6bf191b..6d4cdaaa8ed 100644 --- a/libs/core/shims.d.ts +++ b/libs/core/shims.d.ts @@ -134,8 +134,8 @@ declare namespace basic { /** * Draws an image on the LED screen. - * @param leds the pattern of LED to turn on/off - * @param interval time in milliseconds to pause after drawing + * @param leds the pattern of LED to turn on/off. + * @param interval time in milliseconds to pause after drawing. */ //% help=basic/show-leds //% weight=95 blockGap=8 @@ -170,7 +170,7 @@ declare namespace basic { /** * Shows a sequence of LED screens as an animation. * @param leds pattern of LEDs to turn on/off - * @param interval time in milliseconds between each redraw + * @param interval time in milliseconds between each redraw. */ //% help=basic/show-animation imageLiteral=1 async //% parts="ledmatrix" interval.defl=400 shim=basic::showAnimation @@ -326,7 +326,7 @@ declare namespace input { * Get the magnetic force value in ``micro-Teslas`` (``ÂĩT``). This function is not supported in the simulator. * @param dimension the x, y, or z dimension, eg: Dimension.X */ - //% help=input/magnetic-force weight=51 + //% help=input/magnetic-force weight=54 //% blockId=device_get_magnetic_force block="magnetic force (ÂĩT)|%NAME" blockGap=8 //% parts="compass" //% advanced=true shim=input::magneticForce @@ -337,7 +337,7 @@ declare namespace input { */ //% help=input/calibrate-compass advanced=true //% blockId="input_compass_calibrate" block="calibrate compass" - //% weight=45 shim=input::calibrateCompass + //% weight=55 shim=input::calibrateCompass function calibrateCompass(): void; /** @@ -396,8 +396,9 @@ declare namespace control { * Blocks the current fiber for the given microseconds * @param micros number of micro-seconds to wait. eg: 4 */ - //% help=control/wait-micros weight=29 - //% blockId="control_wait_us" block="wait (Âĩs)%micros" shim=control::waitMicros + //% help=control/wait-micros weight=29 async + //% blockId="control_wait_us" block="wait (Âĩs)%micros" + //% micros.min=0 micros.max=6000 shim=control::waitMicros function waitMicros(micros: int32): void; /** @@ -443,6 +444,12 @@ declare namespace control { //% advanced=true shim=control::deviceName function deviceName(): string; + /** + * Returns the major version of the microbit + */ + //% help=control/hardware-version shim=control::_hardwareVersion + function _hardwareVersion(): string; + /** * Derive a unique, consistent serial number of this device from internal data. */ @@ -485,6 +492,38 @@ declare namespace control { //% shim=control::dmesgPtr function dmesgPtr(str: string, ptr: Object): void; } +declare namespace control { + + /** + * Force GC and dump basic information about heap. + */ + //% shim=control::gc + function gc(): void; + + /** + * Force GC and halt waiting for debugger to do a full heap dump. + */ + //% shim=control::heapDump + function heapDump(): void; + + /** + * Set flags used when connecting an external debugger. + */ + //% shim=control::setDebugFlags + function setDebugFlags(flags: int32): void; + + /** + * Record a heap snapshot to debug memory leaks. + */ + //% shim=control::heapSnapshot + function heapSnapshot(): void; + + /** + * Return true if profiling is enabled in the current build. + */ + //% shim=control::profilingEnabled + function profilingEnabled(): boolean; +} @@ -631,13 +670,22 @@ declare namespace music { * @param enabled whether the built-in speaker is enabled in addition to the sound pin */ //% blockId=music_set_built_in_speaker_enable block="set built-in speaker $enabled" - //% blockGap=8 //% group="micro:bit (V2)" //% parts=builtinspeaker //% help=music/set-built-in-speaker-enabled - //% enabled.shadow=toggleOnOff shim=music::setBuiltInSpeakerEnabled + //% enabled.shadow=toggleOnOff + //% weight=0 shim=music::setBuiltInSpeakerEnabled function setBuiltInSpeakerEnabled(enabled: boolean): void; + /** + * Check whether any sound is being played, no matter the source + */ + //% blockId=music_sound_is_playing block="sound is playing" + //% group="micro:bit (V2)" + //% help=music/is-sound-playing + //% weight=0 shim=music::isSoundPlaying + function isSoundPlaying(): boolean; + /** * Defines an optional sample level to generate during periods of silence. **/ @@ -657,9 +705,8 @@ declare namespace pins { */ //% help=pins/digital-read-pin weight=30 //% blockId=device_get_digital_pin block="digital read|pin %name" blockGap=8 - //% name.fieldEditor="gridpicker" name.fieldOptions.columns=4 - //% name.fieldOptions.tooltips="false" name.fieldOptions.width="250" shim=pins::digitalReadPin - function digitalReadPin(name: DigitalPin): int32; + //% name.shadow=digital_pin_shadow shim=pins::digitalReadPin + function digitalReadPin(name: int32): int32; /** * Set a pin or connector value to either 0 or 1. @@ -669,9 +716,8 @@ declare namespace pins { //% help=pins/digital-write-pin weight=29 //% blockId=device_set_digital_pin block="digital write|pin %name|to %value" //% value.min=0 value.max=1 - //% name.fieldEditor="gridpicker" name.fieldOptions.columns=4 - //% name.fieldOptions.tooltips="false" name.fieldOptions.width="250" shim=pins::digitalWritePin - function digitalWritePin(name: DigitalPin, value: int32): void; + //% name.shadow=digital_pin_shadow shim=pins::digitalWritePin + function digitalWritePin(name: int32, value: int32): void; /** * Read the connector value as analog, that is, as a value comprised between 0 and 1023. @@ -679,9 +725,8 @@ declare namespace pins { */ //% help=pins/analog-read-pin weight=25 //% blockId=device_get_analog_pin block="analog read|pin %name" blockGap="8" - //% name.fieldEditor="gridpicker" name.fieldOptions.columns=4 - //% name.fieldOptions.tooltips="false" name.fieldOptions.width="250" shim=pins::analogReadPin - function analogReadPin(name: AnalogPin): int32; + //% name.shadow=analog_read_write_pin_shadow shim=pins::analogReadPin + function analogReadPin(name: int32): int32; /** * Set the connector value as analog. Value must be comprised between 0 and 1023. @@ -691,31 +736,32 @@ declare namespace pins { //% help=pins/analog-write-pin weight=24 //% blockId=device_set_analog_pin block="analog write|pin %name|to %value" blockGap=8 //% value.min=0 value.max=1023 - //% name.fieldEditor="gridpicker" name.fieldOptions.columns=4 - //% name.fieldOptions.tooltips="false" name.fieldOptions.width="250" shim=pins::analogWritePin - function analogWritePin(name: AnalogPin, value: int32): void; + //% name.shadow=analog_pin_shadow shim=pins::analogWritePin + function analogWritePin(name: int32, value: int32): void; /** * Configure the pulse-width modulation (PWM) period of the analog output in microseconds. * If this pin is not configured as an analog output (using `analog write pin`), the operation has no effect. * @param name analog pin to set period to, eg: AnalogPin.P0 - * @param micros period in micro seconds. eg:20000 + * @param micros period in microseconds. eg:20000 */ //% help=pins/analog-set-period weight=23 blockGap=8 //% blockId=device_set_analog_period block="analog set period|pin %pin|to (Âĩs)%micros" - //% pin.fieldEditor="gridpicker" pin.fieldOptions.columns=4 - //% pin.fieldOptions.tooltips="false" shim=pins::analogSetPeriod - function analogSetPeriod(name: AnalogPin, micros: int32): void; + //% pin.shadow=analog_pin_shadow shim=pins::analogSetPeriod + function analogSetPeriod(name: int32, micros: int32): void; /** * Configure the pin as a digital input and generate an event when the pin is pulsed either high or low. * @param name digital pin to register to, eg: DigitalPin.P0 * @param pulse the value of the pulse, eg: PulseValue.High */ - //% help=pins/on-pulsed weight=22 blockGap=16 advanced=true + //% help=pins/on-pulsed advanced=true //% blockId=pins_on_pulsed block="on|pin %pin|pulsed %pulse" //% pin.fieldEditor="gridpicker" pin.fieldOptions.columns=4 - //% pin.fieldOptions.tooltips="false" pin.fieldOptions.width="250" shim=pins::onPulsed + //% pin.fieldOptions.tooltips="false" pin.fieldOptions.width="250" + //% group="Pulse" + //% weight=25 + //% blockGap=8 shim=pins::onPulsed function onPulsed(name: DigitalPin, pulse: PulseValue, body: () => void): void; /** @@ -723,7 +769,9 @@ declare namespace pins { */ //% help=pins/pulse-duration advanced=true //% blockId=pins_pulse_duration block="pulse duration (Âĩs)" - //% weight=21 blockGap=8 shim=pins::pulseDuration + //% group="Pulse" + //% weight=24 + //% blockGap=8 shim=pins::pulseDuration function pulseDuration(): int32; /** @@ -733,11 +781,13 @@ declare namespace pins { * @param maximum duration in microseconds */ //% blockId="pins_pulse_in" block="pulse in (Âĩs)|pin %name|pulsed %value" - //% weight=20 advanced=true + //% advanced=true //% help=pins/pulse-in - //% name.fieldEditor="gridpicker" name.fieldOptions.columns=4 - //% name.fieldOptions.tooltips="false" name.fieldOptions.width="250" maxDuration.defl=2000000 shim=pins::pulseIn - function pulseIn(name: DigitalPin, value: PulseValue, maxDuration?: int32): int32; + //% name.shadow=digital_pin_shadow + //% group="Pulse" + //% weight=23 + //% blockGap=8 maxDuration.defl=2000000 shim=pins::pulseIn + function pulseIn(name: int32, value: PulseValue, maxDuration?: int32): int32; /** * Write 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). @@ -748,36 +798,38 @@ declare namespace pins { //% blockId=device_set_servo_pin block="servo write|pin %name|to %value" blockGap=8 //% parts=microservo trackArgs=0 //% value.min=0 value.max=180 - //% name.fieldEditor="gridpicker" name.fieldOptions.columns=4 - //% name.fieldOptions.tooltips="false" name.fieldOptions.width="250" shim=pins::servoWritePin - function servoWritePin(name: AnalogPin, value: int32): void; + //% name.shadow=analog_pin_shadow + //% group="Servo" shim=pins::servoWritePin + function servoWritePin(name: int32, value: int32): void; /** * Specifies that a continuous servo is connected. */ //% shim=pins::servoSetContinuous - function servoSetContinuous(name: AnalogPin, value: boolean): void; + function servoSetContinuous(name: int32, value: boolean): void; /** * Configure the IO pin as an analog/pwm output and set a pulse width. The period is 20 ms period and the pulse width is set based on the value given in **microseconds** or `1/1000` milliseconds. * @param name pin name - * @param micros pulse duration in micro seconds, eg:1500 + * @param micros pulse duration in microseconds, eg:1500 */ //% help=pins/servo-set-pulse weight=19 //% blockId=device_set_servo_pulse block="servo set pulse|pin %value|to (Âĩs) %micros" - //% value.fieldEditor="gridpicker" value.fieldOptions.columns=4 - //% value.fieldOptions.tooltips="false" value.fieldOptions.width="250" shim=pins::servoSetPulse - function servoSetPulse(name: AnalogPin, micros: int32): void; + //% value.shadow=analog_pin_shadow + //% group="Servo" shim=pins::servoSetPulse + function servoSetPulse(name: int32, micros: int32): void; /** * Set the pin used when using analog pitch or music. * @param name pin to modulate pitch from */ //% blockId=device_analog_set_pitch_pin block="analog set pitch pin %name" - //% help=pins/analog-set-pitch-pin weight=3 advanced=true - //% name.fieldEditor="gridpicker" name.fieldOptions.columns=4 - //% name.fieldOptions.tooltips="false" name.fieldOptions.width="250" shim=pins::analogSetPitchPin - function analogSetPitchPin(name: AnalogPin): void; + //% help=pins/analog-set-pitch-pin advanced=true + //% name.shadow=analog_pin_shadow + //% group="Pins" + //% weight=12 + //% blockGap=8 shim=pins::analogSetPitchPin + function analogSetPitchPin(name: int32): void; /** * Sets the volume on the pitch pin @@ -798,24 +850,29 @@ declare namespace pins { function analogPitchVolume(): int32; /** - * Emit a plse-width modulation (PWM) signal to the current pitch pin. Use `analog set pitch pin` to define the pitch pin. + * Send a pulse-width modulation (PWM) signal to the current pitch pin. Use `analog set pitch pin` to define the pitch pin. * @param frequency frequency to modulate in Hz. - * @param ms duration of the pitch in milli seconds. + * @param ms duration of the pitch in milliseconds. */ //% blockId=device_analog_pitch block="analog pitch %frequency|for (ms) %ms" - //% help=pins/analog-pitch weight=4 async advanced=true blockGap=8 shim=pins::analogPitch + //% help=pins/analog-pitch async advanced=true + //% group="Pins" + //% weight=14 + //% blockGap=8 shim=pins::analogPitch function analogPitch(frequency: int32, ms: int32): void; /** - * Configure the pull directiion of of a pin. + * Configure the pull direction of of a pin. * @param name pin to set the pull mode on, eg: DigitalPin.P0 * @param pull one of the mbed pull configurations, eg: PinPullMode.PullUp */ - //% help=pins/set-pull weight=3 advanced=true + //% help=pins/set-pull advanced=true //% blockId=device_set_pull block="set pull|pin %pin|to %pull" - //% pin.fieldEditor="gridpicker" pin.fieldOptions.columns=4 - //% pin.fieldOptions.tooltips="false" pin.fieldOptions.width="250" shim=pins::setPull - function setPull(name: DigitalPin, pull: PinPullMode): void; + //% pin.shadow=digital_pin_shadow + //% group="Pins" + //% weight=15 + //% blockGap=8 shim=pins::setPull + function setPull(name: int32, pull: PinPullMode): void; /** * Configure the events emitted by this pin. Events can be subscribed to @@ -823,11 +880,13 @@ declare namespace pins { * @param name pin to set the event mode on, eg: DigitalPin.P0 * @param type the type of events for this pin to emit, eg: PinEventType.Edge */ - //% help=pins/set-events weight=4 advanced=true + //% help=pins/set-events advanced=true //% blockId=device_set_pin_events block="set pin %pin|to emit %type|events" - //% pin.fieldEditor="gridpicker" pin.fieldOptions.columns=4 - //% pin.fieldOptions.tooltips="false" pin.fieldOptions.width="250" shim=pins::setEvents - function setEvents(name: DigitalPin, type: PinEventType): void; + //% pin.shadow=digital_pin_shadow + //% group="Pins" + //% weight=13 + //% blockGap=8 shim=pins::setEvents + function setEvents(name: int32, type: PinEventType): void; /** * Create a new zero-initialized buffer. @@ -842,12 +901,14 @@ declare namespace pins { * @param name pin of Neopixel strip, eg: DigitalPin.P1 * @param value width of matrix (at least ``2``) */ - //% help=pins/neopixel-matrix-width weight=3 advanced=true - //% blockId=pin_neopixel_matrix_width block="neopixel matrix width|pin %pin %width" blockGap=8 - //% pin.fieldEditor="gridpicker" pin.fieldOptions.columns=4 - //% pin.fieldOptions.tooltips="false" pin.fieldOptions.width="250" - //% width.min=2 width.defl=5 shim=pins::setMatrixWidth - function setMatrixWidth(pin: DigitalPin, width?: int32): void; + //% help=pins/neopixel-matrix-width advanced=true + //% blockId=pin_neopixel_matrix_width block="neopixel matrix width|pin %pin %width" + //% pin.shadow=digital_pin_shadow + //% width.min=2 + //% group="Pins" + //% weight=11 + //% blockGap=8 width.defl=5 shim=pins::setMatrixWidth + function setMatrixWidth(pin: int32, width?: int32): void; /** * Read `size` bytes from a 7-bit I2C `address`. @@ -865,8 +926,11 @@ declare namespace pins { * Write to the SPI slave and return the response * @param value Data to be sent to the SPI slave */ - //% help=pins/spi-write weight=5 advanced=true - //% blockId=spi_write block="spi write %value" shim=pins::spiWrite + //% help=pins/spi-write advanced=true + //% blockId=spi_write block="spi write %value" + //% group="SPI" + //% blockGap=8 + //% weight=53 shim=pins::spiWrite function spiWrite(value: int32): int32; /** @@ -881,8 +945,11 @@ declare namespace pins { * Set the SPI frequency * @param frequency the clock frequency, eg: 1000000 */ - //% help=pins/spi-frequency weight=4 advanced=true - //% blockId=spi_frequency block="spi frequency %frequency" shim=pins::spiFrequency + //% help=pins/spi-frequency advanced=true + //% blockId=spi_frequency block="spi frequency %frequency" + //% group="SPI" + //% blockGap=8 + //% weight=55 shim=pins::spiFrequency function spiFrequency(frequency: int32): void; /** @@ -890,40 +957,52 @@ declare namespace pins { * @param bits the number of bits, eg: 8 * @param mode the mode, eg: 3 */ - //% help=pins/spi-format weight=3 advanced=true - //% blockId=spi_format block="spi format|bits %bits|mode %mode" shim=pins::spiFormat + //% help=pins/spi-format advanced=true + //% blockId=spi_format block="spi format|bits %bits|mode %mode" + //% group="SPI" + //% blockGap=8 + //% weight=54 shim=pins::spiFormat function spiFormat(bits: int32, mode: int32): void; /** * Set the MOSI, MISO, SCK pins used by the SPI connection * */ - //% help=pins/spi-pins weight=2 advanced=true + //% help=pins/spi-pins advanced=true //% blockId=spi_pins block="spi set pins|MOSI %mosi|MISO %miso|SCK %sck" - //% mosi.fieldEditor="gridpicker" mosi.fieldOptions.columns=4 - //% mosi.fieldOptions.tooltips="false" mosi.fieldOptions.width="250" - //% miso.fieldEditor="gridpicker" miso.fieldOptions.columns=4 - //% miso.fieldOptions.tooltips="false" miso.fieldOptions.width="250" - //% sck.fieldEditor="gridpicker" sck.fieldOptions.columns=4 - //% sck.fieldOptions.tooltips="false" sck.fieldOptions.width="250" shim=pins::spiPins - function spiPins(mosi: DigitalPin, miso: DigitalPin, sck: DigitalPin): void; + //% mosi.shadow=digital_pin_shadow + //% miso.shadow=digital_pin_shadow + //% sck.shadow=digital_pin_shadow + //% group="SPI" + //% blockGap=8 + //% weight=51 shim=pins::spiPins + function spiPins(mosi: int32, miso: int32, sck: int32): void; /** * Mounts a push button on the given pin */ //% help=pins/push-button advanced=true shim=pins::pushButton - function pushButton(pin: DigitalPin): void; + function pushButton(pin: int32): void; /** * Set the pin used when producing sounds and melodies. Default is P0. * @param name pin to modulate pitch from */ //% blockId=pin_set_audio_pin block="set audio pin $name" - //% help=pins/set-audio-pin weight=3 - //% name.fieldEditor="gridpicker" name.fieldOptions.columns=4 - //% name.fieldOptions.tooltips="false" name.fieldOptions.width="250" - //% weight=1 shim=pins::setAudioPin - function setAudioPin(name: AnalogPin): void; + //% help=pins/set-audio-pin + //% name.shadow=digital_pin_shadow + //% weight=1 + //% blockGap=8 shim=pins::setAudioPin + function setAudioPin(name: int32): void; + + /** + * Sets whether or not audio will be output using a pin on the edge + * connector. + */ + //% blockId=pin_set_audio_pin_enabled + //% block="set audio pin enabled $enabled" + //% weight=0 help=pins/set-audio-pin-enabled shim=pins::setAudioPinEnabled + function setAudioPinEnabled(enabled: boolean): void; } @@ -974,7 +1053,7 @@ declare namespace serial { function writeBuffer(buffer: Buffer): void; /** - * Read multiple characters from the receive buffer. + * Read multiple characters from the receive buffer. * If length is positive, pauses until enough characters are present. * @param length default buffer length */ @@ -1033,6 +1112,10 @@ declare namespace serial { //% blockId=serialSetTxBufferSize block="serial set tx buffer size to $size" //% advanced=true shim=serial::setTxBufferSize function setTxBufferSize(size: uint8): void; + + /** Send DMESG debug buffer over serial. */ + //% shim=serial::writeDmesg + function writeDmesg(): void; } diff --git a/libs/core/soundexpressions.ts b/libs/core/soundexpressions.ts index 769fa89d92a..95b46c42080 100644 --- a/libs/core/soundexpressions.ts +++ b/libs/core/soundexpressions.ts @@ -4,18 +4,32 @@ //% fixedInstances //% blockNamespace=music //% group="micro:bit (V2)" -class SoundExpression { +class SoundExpression extends music.Playable { constructor(private notes: string) { + super() + } + + _play(mode: music.PlaybackMode) { + if (mode === music.PlaybackMode.InBackground) { + this.play(); + } else if (mode === music.PlaybackMode.UntilDone) { + this.playUntilDone(); + } else { + this.loop(); + } } /** * Starts to play a sound expression. */ //% block="play sound $this" + //% this.label="sound" //% weight=80 //% blockGap=8 //% help=music/play //% group="micro:bit (V2)" + //% parts=builtinspeaker + //% deprecated=1 play() { music.__playSoundExpression(this.notes, false) } @@ -24,13 +38,60 @@ class SoundExpression { * Plays a sound expression until finished */ //% block="play sound $this until done" + //% this.label="sound" //% weight=81 //% blockGap=8 //% help=music/play-until-done //% group="micro:bit (V2)" + //% parts=builtinspeaker + //% deprecated=1 playUntilDone() { music.__playSoundExpression(this.notes, true) } + + getNotes() { + return this.notes; + } +} + +enum WaveShape { + //% block="sine" + Sine = 0, + //% block="sawtooth" + Sawtooth = 1, + //% block="triangle" + Triangle = 2, + //% block="square" + Square = 3, + //% block="noise" + Noise = 4 +} + +enum InterpolationCurve { + //% block="linear" + Linear, + //% block="curve" + Curve, + //% block="logarithmic" + Logarithmic +} + +enum SoundExpressionEffect { + //% block="none" + None = 0, + //% block="vibrato" + Vibrato = 1, + //% block="tremolo" + Tremolo = 2, + //% block="warble" + Warble = 3 +} + +enum SoundExpressionPlayMode { + //% block="until done" + UntilDone, + //% block="in background" + InBackground } namespace soundExpression { @@ -54,4 +115,405 @@ namespace soundExpression { export const twinkle = new SoundExpression("twinkle"); //% fixedInstance whenUsed block="{id:soundexpression}yawn" export const yawn = new SoundExpression("yawn"); + + export enum InterpolationEffect { + None = 0, + Linear = 1, + Curve = 2, + ExponentialRising = 5, + ExponentialFalling = 6, + ArpeggioRisingMajor = 8, + ArpeggioRisingMinor = 10, + ArpeggioRisingDiminished = 12, + ArpeggioRisingChromatic = 14, + ArpeggioRisingWholeTone = 16, + ArpeggioFallingMajor = 9, + ArpeggioFallingMinor = 11, + ArpeggioFallingDiminished = 13, + ArpeggioFallingChromatic = 15, + ArpeggioFallingWholeTone = 17, + Logarithmic = 18 + } + + export class Sound { + src: string; + + constructor() { + this.src = "000000000000000000000000000000000000000000000000000000000000000000000000" + } + + get wave(): WaveShape { + return this.getValue(0, 1); + } + + set wave(value: WaveShape) { + this.setValue(0, Math.constrain(value, 0, 4), 1); + } + + get volume() { + return this.getValue(1, 4); + } + + set volume(value: number) { + this.setValue(1, Math.constrain(value, 0, 1023), 4); + } + + get frequency() { + return this.getValue(5, 4); + } + + set frequency(value: number) { + this.setValue(5, value, 4); + } + + get duration() { + return this.getValue(9, 4); + } + + set duration(value: number) { + this.setValue(9, value, 4); + } + + get shape(): InterpolationEffect { + return this.getValue(13, 2); + } + + set shape(value: InterpolationEffect) { + this.setValue(13, value, 2); + } + + get endFrequency() { + return this.getValue(18, 4); + } + + set endFrequency(value: number) { + this.setValue(18, value, 4); + } + + get endVolume() { + return this.getValue(26, 4); + } + + set endVolume(value: number) { + this.setValue(26, Math.constrain(value, 0, 1023), 4); + } + + get steps() { + return this.getValue(30, 4); + } + + set steps(value: number) { + this.setValue(30, value, 4); + } + + get fx(): SoundExpressionEffect { + return this.getValue(34, 2); + } + + set fx(value: SoundExpressionEffect) { + this.setValue(34, Math.constrain(value, 0, 3), 2); + } + + get fxParam() { + return this.getValue(36, 4); + } + + set fxParam(value: number) { + this.setValue(36, value, 4); + } + + get fxnSteps() { + return this.getValue(40, 4); + } + + set fxnSteps(value: number) { + this.setValue(40, value, 4); + } + + get frequencyRandomness() { + return this.getValue(44, 4); + } + + set frequencyRandomness(value: number) { + this.setValue(44, value, 4); + } + + get endFrequencyRandomness() { + return this.getValue(48, 4); + } + + set endFrequencyRandomness(value: number) { + this.setValue(48, value, 4); + } + + get volumeRandomness() { + return this.getValue(52, 4); + } + + set volumeRandomness(value: number) { + this.setValue(52, value, 4); + } + + get endVolumeRandomness() { + return this.getValue(56, 4); + } + + set endVolumeRandomness(value: number) { + this.setValue(56, value, 4); + } + + get durationRandomness() { + return this.getValue(60, 4); + } + + set durationRandomness(value: number) { + this.setValue(60, value, 4); + } + + get fxParamRandomness() { + return this.getValue(64, 4); + } + + set fxParamRandomness(value: number) { + this.setValue(64, value, 4); + } + + get fxnStepsRandomness() { + return this.getValue(68, 4); + } + + set fxnStepsRandomness(value: number) { + this.setValue(68, value, 4); + } + + copy() { + const result = new Sound(); + result.src = this.src.slice(0); + return result; + } + + protected setValue(offset: number, value: number, length: number) { + value = Math.constrain(value | 0, 0, Math.pow(10, length) - 1); + this.src = this.src.substr(0, offset) + formatNumber(value, length) + this.src.substr(offset + length); + } + + protected getValue(offset: number, length: number) { + return parseInt(this.src.substr(offset, length)); + } + } + + function formatNumber(num: number, length: number) { + let result = num + ""; + while (result.length < length) result = "0" + result; + return result; + } + + export function playSound(toPlay: Sound | Sound[]) { + let src = ""; + if (Array.isArray(toPlay)) { + src = (toPlay as Sound[]).map(s => s.src).join(","); + } + else { + src = (toPlay as Sound).src; + } + + new SoundExpression(src).playUntilDone(); + } +} + +namespace music { + /** + * Play a sound effect from a sound expression string. + * @param sound the sound expression string + * @param mode the play mode, play until done or in the background + */ + //% blockId=soundExpression_playSoundEffect + //% block="play sound $sound $mode" + //% sound.label="sound" + //% sound.shadow=soundExpression_createSoundEffect + //% weight=100 help=music/play-sound-effect + //% blockGap=8 + //% deprecated=1 + //% group="micro:bit (V2)" + export function playSoundEffect(sound: string, mode: SoundExpressionPlayMode) { + if (mode === SoundExpressionPlayMode.InBackground) { + new SoundExpression(sound).play(); + } + else { + new SoundExpression(sound).playUntilDone(); + } + } + + /** + * Create a sound expression from a set of sound effect parameters. + * @param waveShape waveform of the sound effect + * @param startFrequency starting frequency for the sound effect waveform + * @param endFrequency ending frequency for the sound effect waveform + * @param startVolume starting volume of the sound, or starting amplitude + * @param endVolume ending volume of the sound, or ending amplitude + * @param duration the amount of time in milliseconds (ms) that sound will play for + * @param effect the effect to apply to the waveform or volume + * @param interpolation interpolation method for frequency scaling + */ + //% blockId=soundExpression_createSoundEffect + //% help=music/create-sound-effect + //% block="$waveShape|| start frequency $startFrequency end frequency $endFrequency duration $duration start volume $startVolume end volume $endVolume effect $effect interpolation $interpolation" + //% startFrequency.label="start frequency" endFrequency.label="end frequency" duration.label="duration" startVolume.label="start volume" endVolume.label="end volume" + //% waveShape.defl=WaveShape.Sine + //% waveShape.fieldEditor=soundeffect + //% startFrequency.defl=5000 + //% startFrequency.min=0 + //% startFrequency.max=5000 + //% endFrequency.defl=0 + //% endFrequency.min=0 + //% endFrequency.max=5000 + //% startVolume.defl=255 + //% startVolume.min=0 + //% startVolume.max=255 + //% endVolume.defl=0 + //% endVolume.min=0 + //% endVolume.max=255 + //% duration.defl=500 + //% duration.min=1 + //% duration.max=9999 + //% effect.defl=SoundExpressionEffect.None + //% interpolation.defl=InterpolationCurve.Linear + //% compileHiddenArguments=true + //% inlineInputMode="variable" + //% inlineInputModeLimit=3 + //% expandableArgumentBreaks="3,5" + //% group="micro:bit (V2)" + //% deprecated=1 + export function createSoundEffect(waveShape: WaveShape, startFrequency: number, endFrequency: number, startVolume: number, endVolume: number, duration: number, effect: SoundExpressionEffect, interpolation: InterpolationCurve): string { + let src = "000000000000000000000000000000000000000000000000000000000000000000000000"; + src = setValue(src, 0, Math.constrain(waveShape, 0, 4), 1); + src = setValue(src, 1, Math.constrain(((startVolume / 255) * 1023) | 0, 0, 1023), 4); + src = setValue(src, 5, startFrequency, 4); + src = setValue(src, 9, duration, 4); + src = setValue(src, 18, endFrequency, 4); + src = setValue(src, 26, Math.constrain(((endVolume / 255) * 1023) | 0, 0, 1023), 4); + src = setValue(src, 34, Math.constrain(effect, 0, 3), 2); + + + switch (interpolation) { + case InterpolationCurve.Linear: + src = setValue(src, 13, soundExpression.InterpolationEffect.Linear, 2); + src = setValue(src, 30, 128, 4); + break; + case InterpolationCurve.Curve: + src = setValue(src, 13, soundExpression.InterpolationEffect.Curve, 2); + src = setValue(src, 30, 90, 4); + break; + case InterpolationCurve.Logarithmic: + src = setValue(src, 13, soundExpression.InterpolationEffect.Logarithmic, 2); + src = setValue(src, 30, 90, 4); + break; + } + + switch (effect) { + case SoundExpressionEffect.Vibrato: + src = setValue(src, 36, DAL.SFX_DEFAULT_VIBRATO_PARAM, 4); + src = setValue(src, 40, DAL.SFX_DEFAULT_VIBRATO_STEPS, 4); + break; + case SoundExpressionEffect.Tremolo: + src = setValue(src, 36, DAL.SFX_DEFAULT_TREMOLO_PARAM, 4); + src = setValue(src, 40, DAL.SFX_DEFAULT_TREMOLO_STEPS, 4); + break; + case SoundExpressionEffect.Warble: + src = setValue(src, 36, DAL.SFX_DEFAULT_WARBLE_PARAM, 4); + src = setValue(src, 40, DAL.SFX_DEFAULT_WARBLE_STEPS, 4); + break; + } + + return src; + } + + /** + * Create a sound expression from a set of sound effect parameters. + * @param waveShape waveform of the sound effect + * @param startFrequency starting frequency for the sound effect waveform + * @param endFrequency ending frequency for the sound effect waveform + * @param startVolume starting volume of the sound, or starting amplitude + * @param endVolume ending volume of the sound, or ending amplitude + * @param duration the amount of time in milliseconds (ms) that sound will play for + * @param effect the effect to apply to the waveform or volume + * @param interpolation interpolation method for frequency scaling + */ + //% blockId=soundExpression_createSoundExpression + //% help=music/create-sound-expression + //% block="$waveShape|| start frequency $startFrequency end frequency $endFrequency duration $duration start volume $startVolume end volume $endVolume effect $effect interpolation $interpolation" + //% startFrequency.label="start frequency" endFrequency.label="end frequency" duration.label="duration" startVolume.label="start volume" endVolume.label="end volume" + //% waveShape.defl=WaveShape.Sine + //% waveShape.fieldEditor=soundeffect + //% startFrequency.defl=5000 + //% startFrequency.min=0 + //% startFrequency.max=5000 + //% endFrequency.defl=0 + //% endFrequency.min=0 + //% endFrequency.max=5000 + //% startVolume.defl=255 + //% startVolume.min=0 + //% startVolume.max=255 + //% endVolume.defl=0 + //% endVolume.min=0 + //% endVolume.max=255 + //% duration.defl=500 + //% duration.min=1 + //% duration.max=9999 + //% effect.defl=SoundExpressionEffect.None + //% interpolation.defl=InterpolationCurve.Linear + //% compileHiddenArguments=true + //% inlineInputMode="variable" + //% inlineInputModeLimit=3 + //% expandableArgumentBreaks="3,5" + //% duplicateWithToolboxParent=music_playable_play + //% duplicateWithToolboxParentArgument=toPlay + //% duplicateShadowOnDrag + //% group="micro:bit (V2)" + export function createSoundExpression(waveShape: WaveShape, startFrequency: number, endFrequency: number, startVolume: number, endVolume: number, duration: number, effect: SoundExpressionEffect, interpolation: InterpolationCurve): SoundExpression { + return new SoundExpression(createSoundEffect(waveShape, startFrequency, endFrequency, startVolume, endVolume, duration, effect, interpolation)); + } + + function setValue(src: string, offset: number, value: number, length: number) { + value = Math.constrain(value | 0, 0, Math.pow(10, length) - 1); + return src.substr(0, offset) + formatNumber(value, length) + src.substr(offset + length); + } + + function formatNumber(num: number, length: number) { + let result = num + ""; + while (result.length < length) result = "0" + result; + return result; + } + + /** + * Get the sound expression string for a built-in a sound effect. + * @param soundExpression a sound expression for a built-in sound effect + */ + //% blockId=soundExpression_builtinSoundEffect + //% block="$soundExpression" + //% blockGap=8 + //% group="micro:bit (V2)" + //% toolboxParent=soundExpression_playSoundEffect + //% toolboxParentArgument=sound + //% weight=98 help=music/builtin-sound-effect + //% deprecated=1 + export function builtinSoundEffect(soundExpression: SoundExpression) { + return soundExpression.getNotes(); + } + + /** + * Get the sound expression string for a built-in sound effect. + * @param soundExpression a sound expression for a built-in sound effect + */ + //% blockId=soundExpression_builtinPlayableSoundEffect + //% block="$soundExpression" + //% blockGap=8 + //% group="micro:bit (V2)" + //% toolboxParent=music_playable_play + //% toolboxParentArgument=toPlay + //% duplicateShadowOnDrag + //% weight=98 help=music/builtin-sound-effect + export function builtinPlayableSoundEffect(soundExpression: SoundExpression) { + return soundExpression; + } } \ No newline at end of file diff --git a/libs/datalogger/_locales/datalogger-jsdoc-strings.json b/libs/datalogger/_locales/datalogger-jsdoc-strings.json new file mode 100644 index 00000000000..2695091c25b --- /dev/null +++ b/libs/datalogger/_locales/datalogger-jsdoc-strings.json @@ -0,0 +1,45 @@ +{ + "datalogger": "Log data to flash storage", + "datalogger.createCV": "A column and value to log to flash storage\n\n\n@returns A new value that can be stored in flash storage using log data", + "datalogger.createCV|param|column": "the column to set", + "datalogger.createCV|param|value": "the value to set.", + "datalogger.deleteLog": "Delete all existing logs, including column headers. By default this only marks the log as\noverwriteable / deletable in the future.", + "datalogger.deleteLog|param|deleteType": "optional set whether a deletion will be fast or full", + "datalogger.getNumberOfRows": "Number of rows currently used by the datalogger, start counting at fromRowIndex\nTreats the header as the first row\n\n@returns header + rows", + "datalogger.getNumberOfRows|param|fromRowIndex": "0-based index of start", + "datalogger.getRows": "Get all rows seperated by a newline & each column seperated by a comma.\nStarting at the 0-based index fromRowIndex & counting inclusively until nRows.\n\n\n@returns String where newlines denote rows & commas denote columns", + "datalogger.getRows|param|fromRowIndex": "0-based index of start", + "datalogger.getRows|param|nRows": "inclusive count from fromRowIndex", + "datalogger.includeTimestamp": "Set the format for timestamps", + "datalogger.includeTimestamp|param|format": "Format in which to show the timestamp. Setting FlashLogTimeStampFormat.None will disable the timestamp.", + "datalogger.log": "Log data to flash storage", + "datalogger.logData": "Log data to flash storage", + "datalogger.logData|param|data": "Array of data to be logged to flash storage", + "datalogger.log|param|data1": "First column and value to be logged", + "datalogger.log|param|data10": "[optional] tenth column and value to be logged", + "datalogger.log|param|data2": "[optional] second column and value to be logged", + "datalogger.log|param|data3": "[optional] third column and value to be logged", + "datalogger.log|param|data4": "[optional] fourth column and value to be logged", + "datalogger.log|param|data5": "[optional] fifth column and value to be logged", + "datalogger.log|param|data6": "[optional] sixth column and value to be logged", + "datalogger.log|param|data7": "[optional] seventh column and value to be logged", + "datalogger.log|param|data8": "[optional] eighth column and value to be logged", + "datalogger.log|param|data9": "[optional] ninth column and value to be logged", + "datalogger.mirrorToSerial": "Set whether data is mirrored to serial or not.", + "datalogger.mirrorToSerial|param|on": "if true, data that is logged will be mirrored to serial", + "datalogger.onLogFull": "Register an event to run when no more data can be logged.", + "datalogger.onLogFull|param|handler": "code to run when the log is full and no more data can be stored.", + "datalogger.setColumnTitles": "Set the columns for future data logging", + "datalogger.setColumnTitles|param|col1": "Title for first column to be added", + "datalogger.setColumnTitles|param|col10": "Title for tenth column to be added", + "datalogger.setColumnTitles|param|col2": "Title for second column to be added", + "datalogger.setColumnTitles|param|col3": "Title for third column to be added", + "datalogger.setColumnTitles|param|col4": "Title for fourth column to be added", + "datalogger.setColumnTitles|param|col5": "Title for fifth column to be added", + "datalogger.setColumnTitles|param|col6": "Title for sixth column to be added", + "datalogger.setColumnTitles|param|col7": "Title for seventh column to be added", + "datalogger.setColumnTitles|param|col8": "Title for eighth column to be added", + "datalogger.setColumnTitles|param|col9": "Title for ninth column to be added", + "datalogger.setColumns": "Set the columns for future data logging", + "datalogger.setColumns|param|cols": "Array of the columns that will be logged." +} \ No newline at end of file diff --git a/libs/datalogger/_locales/datalogger-strings.json b/libs/datalogger/_locales/datalogger-strings.json new file mode 100644 index 00000000000..2c1399f72a2 --- /dev/null +++ b/libs/datalogger/_locales/datalogger-strings.json @@ -0,0 +1,17 @@ +{ + "datalogger.DeleteType.Fast|block": "fast", + "datalogger.DeleteType.Full|block": "full", + "datalogger._columnField|block": "$column", + "datalogger.createCV|block": "column $column value $value", + "datalogger.deleteLog|block": "delete log||$deleteType", + "datalogger.includeTimestamp|block": "set timestamp $format", + "datalogger.logData|block": "log data array $data", + "datalogger.log|block": "log data $data1||$data2 $data3 $data4 $data5 $data6 $data7 $data8 $data9 $data10", + "datalogger.mirrorToSerial|block": "mirror data to serial $on", + "datalogger.onLogFull|block": "on log full", + "datalogger.setColumnTitles|block": "set columns $col1||$col2 $col3 $col4 $col5 $col6 $col7 $col8 $col9 $col10", + "datalogger.setColumns|block": "set columns $cols", + "datalogger|block": "Data Logger", + "{id:category}Datalogger": "Datalogger", + "{id:group}micro:bit (V2)": "micro:bit (V2)" +} \ No newline at end of file diff --git a/libs/datalogger/datalogger.ts b/libs/datalogger/datalogger.ts new file mode 100644 index 00000000000..2daddd47d90 --- /dev/null +++ b/libs/datalogger/datalogger.ts @@ -0,0 +1,316 @@ +/** + * Log data to flash storage + */ +//% block="Data Logger" +//% icon="\uf0ce" +//% color="#378273" +namespace datalogger { + export enum DeleteType { + //% block="fast" + Fast, + //% block="full" + Full + } + + let onLogFullHandler: () => void; + let _disabled = false; + + let initialized = false; + function init() { + if (initialized) + return; + initialized = true; + + includeTimestamp(FlashLogTimeStampFormat.Seconds); + mirrorToSerial(false); + + control.onEvent(DAL.MICROBIT_ID_LOG, DAL.MICROBIT_LOG_EVT_LOG_FULL, () => { + _disabled = true; + if (onLogFullHandler) { + onLogFullHandler(); + } else { + basic.showLeds(` + # . . . # + # # . # # + . . . . . + . # # # . + # . . . # + `); + basic.pause(1000); + basic.clearScreen(); + basic.showString("928"); + } + }); + } + + + export class ColumnValue { + public value: string; + constructor( + public column: string, + value: any + ) { + this.value = "" + value; + } + } + + /** + * A column and value to log to flash storage + * @param column the column to set + * @param value the value to set. + * @returns A new value that can be stored in flash storage using log data + */ + //% block="column $column value $value" + //% column.label="column" value.label="value" + //% value.shadow=math_number + //% column.shadow=datalogger_columnfield + //% blockId=dataloggercreatecolumnvalue + //% group="micro:bit (V2)" + //% weight=80 help=datalogger/create-cv + export function createCV(column: string, value: any): ColumnValue { + return new ColumnValue(column, value); + } + + //% block="$column" + //% blockId=datalogger_columnfield + //% group="micro:bit (V2)" + //% blockHidden=true shim=TD_ID + //% column.fieldEditor="autocomplete" column.fieldOptions.decompileLiterals=true + //% column.fieldOptions.key="dataloggercolumn" + export function _columnField(column: string) { + return column + } + + /** + * Log data to flash storage + * @param data Array of data to be logged to flash storage + */ + //% block="log data array $data" + //% data.label="value" + //% blockId=dataloggerlogdata + //% data.shadow=lists_create_with + //% data.defl=dataloggercreatecolumnvalue + //% group="micro:bit (V2)" + //% blockHidden=true + //% weight=100 + export function logData(data: ColumnValue[]): void { + if (!data || !data.length) + return; + init(); + + if (_disabled) + return; + + flashlog.beginRow(); + for (const cv of data) { + flashlog.logData(cv.column, cv.value); + } + flashlog.endRow(); + } + + /** + * Log data to flash storage + * @param data1 First column and value to be logged + * @param data2 [optional] second column and value to be logged + * @param data3 [optional] third column and value to be logged + * @param data4 [optional] fourth column and value to be logged + * @param data5 [optional] fifth column and value to be logged + * @param data6 [optional] sixth column and value to be logged + * @param data7 [optional] seventh column and value to be logged + * @param data8 [optional] eighth column and value to be logged + * @param data9 [optional] ninth column and value to be logged + * @param data10 [optional] tenth column and value to be logged + */ + //% block="log data $data1||$data2 $data3 $data4 $data5 $data6 $data7 $data8 $data9 $data10" + //% data1.label="data 1" data2.label="data 2" data3.label="data 3" data4.label="data 4" data5.label="data 5" data6.label="data 6" data7.label="data 7" data8.label="data 8" data9.label="data 9" data10.label="data 10" + //% blockId=dataloggerlog + //% data1.shadow=dataloggercreatecolumnvalue + //% data2.shadow=dataloggercreatecolumnvalue + //% data3.shadow=dataloggercreatecolumnvalue + //% data4.shadow=dataloggercreatecolumnvalue + //% data5.shadow=dataloggercreatecolumnvalue + //% data6.shadow=dataloggercreatecolumnvalue + //% data7.shadow=dataloggercreatecolumnvalue + //% data8.shadow=dataloggercreatecolumnvalue + //% data9.shadow=dataloggercreatecolumnvalue + //% data10.shadow=dataloggercreatecolumnvalue + //% inlineInputMode="variable" + //% inlineInputModeLimit=1 + //% group="micro:bit (V2)" + //% weight=100 help=datalogger/log + export function log( + data1: datalogger.ColumnValue, + data2?: datalogger.ColumnValue, + data3?: datalogger.ColumnValue, + data4?: datalogger.ColumnValue, + data5?: datalogger.ColumnValue, + data6?: datalogger.ColumnValue, + data7?: datalogger.ColumnValue, + data8?: datalogger.ColumnValue, + data9?: datalogger.ColumnValue, + data10?: datalogger.ColumnValue + ): void { + logData( + [ + data1, + data2, + data3, + data4, + data5, + data6, + data7, + data8, + data9, + data10, + ].filter(el => !!el) + ); + } + + /** + * Set the columns for future data logging + * @param cols Array of the columns that will be logged. + */ + //% block="set columns $cols" + //% cols.label="value" + //% blockId=dataloggersetcolumns + //% data.shadow=list_create_with + //% data.defl=datalogger_columnfield + //% group="micro:bit (V2)" + //% blockHidden=true + //% weight=70 + export function setColumns(cols: string[]): void { + if (!cols) + return; + + logData(cols.map(col => createCV(col, ""))); + } + + /** + * Set the columns for future data logging + * @param col1 Title for first column to be added + * @param col2 Title for second column to be added + * @param col3 Title for third column to be added + * @param col4 Title for fourth column to be added + * @param col5 Title for fifth column to be added + * @param col6 Title for sixth column to be added + * @param col7 Title for seventh column to be added + * @param col8 Title for eighth column to be added + * @param col9 Title for ninth column to be added + * @param col10 Title for tenth column to be added + */ + //% block="set columns $col1||$col2 $col3 $col4 $col5 $col6 $col7 $col8 $col9 $col10" + //% col1.label="column 1" col2.label="column 2" col3.label="column 3" col4.label="column 4" col5.label="column 5" col6.label="column 6" col7.label="column 7" col8.label="column 8" col9.label="column 9" col10.label="column 10" + //% blockId=dataloggersetcolumntitles + //% inlineInputMode="variable" + //% inlineInputModeLimit=1 + //% group="micro:bit (V2)" + //% weight=70 help=datalogger/set-column-titles + //% col1.shadow=datalogger_columnfield + //% col2.shadow=datalogger_columnfield + //% col3.shadow=datalogger_columnfield + //% col4.shadow=datalogger_columnfield + //% col5.shadow=datalogger_columnfield + //% col6.shadow=datalogger_columnfield + //% col7.shadow=datalogger_columnfield + //% col8.shadow=datalogger_columnfield + //% col9.shadow=datalogger_columnfield + //% col10.shadow=datalogger_columnfield + export function setColumnTitles( + col1: string, + col2?: string, + col3?: string, + col4?: string, + col5?: string, + col6?: string, + col7?: string, + col8?: string, + col9?: string, + col10?: string + ): void { + logData( + [col1, col2, col3, col4, col5, col6, col7, col8, col9, col10] + .filter(el => !!el) + .map(col => createCV(col, "")) + ); + } + + /** + * Delete all existing logs, including column headers. By default this only marks the log as + * overwriteable / deletable in the future. + * @param deleteType optional set whether a deletion will be fast or full + */ + //% block="delete log||$deleteType" + //% blockId=dataloggerdeletelog + //% group="micro:bit (V2)" + //% weight=60 help=datalogger/delete-log + export function deleteLog(deleteType?: DeleteType): void { + init(); + flashlog.clear(deleteType === DeleteType.Full); + _disabled = false; + } + + /** + * Register an event to run when no more data can be logged. + * @param handler code to run when the log is full and no more data can be stored. + */ + //% block="on log full" + //% blockId="on log full" + //% group="micro:bit (V2)" + //% weight=40 help=datalogger/on-log-full + export function onLogFull(handler: () => void): void { + init(); + onLogFullHandler = handler; + } + + /** + * Set the format for timestamps + * @param format Format in which to show the timestamp. Setting FlashLogTimeStampFormat.None will disable the timestamp. + */ + //% block="set timestamp $format" + //% blockId=dataloggertoggleincludetimestamp + //% format.defl=FlashLogTimeStampFormat.None + //% group="micro:bit (V2)" + //% weight=30 help=datalogger/include-timestamp + export function includeTimestamp(format: FlashLogTimeStampFormat): void { + init(); + flashlog.setTimeStamp(format); + } + + /** + * Set whether data is mirrored to serial or not. + * @param on if true, data that is logged will be mirrored to serial + */ + //% block="mirror data to serial $on" + //% on.label="value" + //% blockId=dataloggertogglemirrortoserial + //% on.shadow=toggleOnOff + //% on.defl=false + //% weight=25 help=datalogger/mirror-to-serial + export function mirrorToSerial(on: boolean): void { + // TODO:/note intentionally does not have group, as having the same group for all + // blocks in a category causes the group to be elided. + init(); + flashlog.setSerialMirroring(on); + } + + /** + * Number of rows currently used by the datalogger, start counting at fromRowIndex + * Treats the header as the first row + * @param fromRowIndex 0-based index of start + * @returns header + rows + */ + export function getNumberOfRows(fromRowIndex: number = 0): number { + return flashlog.getNumberOfRows(fromRowIndex); + } + + /** + * Get all rows seperated by a newline & each column seperated by a comma. + * Starting at the 0-based index fromRowIndex & counting inclusively until nRows. + * @param fromRowIndex 0-based index of start + * @param nRows inclusive count from fromRowIndex + * @returns String where newlines denote rows & commas denote columns + */ + export function getRows(fromRowIndex: number, nRows: number): string { + return flashlog.getRows(fromRowIndex, nRows); + } +} diff --git a/libs/datalogger/docs/reference/datalogger.md b/libs/datalogger/docs/reference/datalogger.md new file mode 100644 index 00000000000..eb68b56be15 --- /dev/null +++ b/libs/datalogger/docs/reference/datalogger.md @@ -0,0 +1,70 @@ +# Datalogger + +The Datalogger extension logs user data to the flash storage on the @boardname@. Each data item is stored in a column of as part of a row of data. Data is logged to storage by rows. The columns can have names to specify the meaning of data item values. + +### ~ reminder + +#### Works with micro:bit V2 + +![works with micro:bit V2 only image](/static/v2/v2-only.png) + +Using these blocks requires the [micro:bit V2](/device/v2) hardware. If you use any blocks that attempt access flash memory on a micro:bit v1 board, you will see the **927** error code on the screen. + +### ~ + +## Data logs + +A data log will represent a table of information like: + +| Temperature | Acceleration | Light level | +| - | - | - | +| 20 | 3 |123 | +| 23 | 2 | 210 | +| 19 | 4 | 98 | +
+ +A data item consists of value name, which is it's assigned column too, and the item's value. They are called "column-value" items. Here's how a column-value item is created. + +```blocks +let item = datalogger.createCV("temperature", input.temperature()) +``` +The order and the names of the data items are set using column titles. + +```blocks +datalogger.setColumnTitles("temperature", "acceleration", "light") +``` + +Data items are logged to storage as a row. Here's an example of logging a row of data. Each different data value is associated with its colunm before it's logged. + +```blocks +let temp = datalogger.createCV("temperature", input.temperature()) +let accel = datalogger.createCV("acceleration", input.acceleration(Dimension.X)) +let lite = datalogger.createCV("light", input.lightLevel()) +datalogger.log(temp, accel, lite) +``` + +## Blocks in this extension + +```cards +datalogger.createCV("", 0) +datalogger.setColumnTitles([""]) +datalogger.log(datalogger.createCV("", null)) +datalogger.deleteLog(DeleteType.Fast) +datalogger.includeTimestamp(FlashLogTimeStampFormat.None) +datalogger.onLogFull(function() {}) +datalogger.mirrorToSerial(false) +``` + +## See also + +[create cv](/reference/datalogger/create-cv), +[set column titles](/reference/datalogger/set-column-titles), +[log](/reference/datalogger/log), +[delete log](/reference/datalogger/delete-log), +[include timestamp](/reference/datalogger/include-timestamp), +[on log full](/reference/datalogger/send-to-console), +[mirror to serial](/reference/datalogger/mirror-to-serial) + +```package +datalogger +``` diff --git a/libs/datalogger/docs/reference/datalogger/create-cv.md b/libs/datalogger/docs/reference/datalogger/create-cv.md new file mode 100644 index 00000000000..9f8401a3c8b --- /dev/null +++ b/libs/datalogger/docs/reference/datalogger/create-cv.md @@ -0,0 +1,72 @@ +# create CV + +Create a column-value data log item for a data value. + +```sig +datalogger.createCV("", 0) +``` + +Data values that are written to the data log are assigned to a _column_ in order to identify what their value is related to. Before logging a data value, it is formatted as a "CV" or "column-value" data item. A column name is attached to a data value this way. + +A data log entry is written to the data log as an array of one or more "column-value" data item objects. This function creates the data item for the value you want to include in a log entry. + +## Parameters + +* **column**: a [string](types/string) name that identifies the data value. +* **value**: a data value of _any_ type that is logged with the `column` name. + +## Example + +### Button states + +Record the state of the buttons on the @boardname@ every 500 milliseconds. + +```blocks +loops.everyInterval(500, function () { + datalogger.logData([datalogger.createCV("Button A", input.buttonIsPressed(Button.A)), datalogger.createCV("Button B", input.buttonIsPressed(Button.B))]) +}) +``` + +### Mood experiment + +Create an experiment to record a person's moods and relate them to current environmental factors. Set 3 button options for moods of "happy", "sad", and "angry". When the user signals their mood by pressing a button, read the current temperature, light level, and sound level to establish a relationship between those factors and mood. + +```blocks +input.onButtonPressed(Button.A, function () { + logMood("happy") +}) +function logMood (mood: string) { + columns[0] = datalogger.createCV("mood", mood) + columns[1] = datalogger.createCV("light", input.lightLevel()) + columns[2] = datalogger.createCV("sound", input.soundLevel()) + columns[3] = datalogger.createCV("temperature", input.temperature()) + datalogger.logData(columns) +} +input.onButtonPressed(Button.AB, function () { + logMood("angry") +}) +input.onButtonPressed(Button.B, function () { + logMood("sad") +}) +let columns: datalogger.ColumnValue[] = [] +datalogger.setColumns([ +"mood", +"light", +"sound", +"temperature" +]) +columns = [ +datalogger.createCV("", 0), +datalogger.createCV("", 0), +datalogger.createCV("", 0), +datalogger.createCV("", 0) +] +``` + +## See also + +[log data](/reference/datalogger/log-data), [set columns](/reference/datalogger/set-columns) + +```package +datalogger +``` \ No newline at end of file diff --git a/libs/datalogger/docs/reference/datalogger/delete-log.md b/libs/datalogger/docs/reference/datalogger/delete-log.md new file mode 100644 index 00000000000..1ca72a3cb4d --- /dev/null +++ b/libs/datalogger/docs/reference/datalogger/delete-log.md @@ -0,0 +1,33 @@ +# delete Log + +Delete the contents of the data log from the flash memory on the @boardname@. + +```sig +datalogger.deleteLog(DeleteType.Fast) +``` + +If the data log becomes full or you decide to start logging again from the beginning, you can delete the contents of the log. There are two methods for deleting the log. You can use the `fast` method to simply start logging again at the beginning of the log and overwrite the existing log entries. If you want to first clear the log by deleting all of the log entries before writing to the log again, use the `full` method. + +## Parameters + +* **deleteType**: (optional) the method used to delete the log. There are two methods: +>* ``fast``: (default) mark the log to be overwitten with any new log data items. +>* ``full``: delete all data items from the log. + +## Example + +Clear the entire data log when it becomes full. + +```blocks +datalogger.onLogFull(function() { + datalogger.deleteLog() +}) +``` + +## See also + +[on log full](/reference/datalogger/on-log-full) + +```package +datalogger +``` \ No newline at end of file diff --git a/libs/datalogger/docs/reference/datalogger/include-timestamp.md b/libs/datalogger/docs/reference/datalogger/include-timestamp.md new file mode 100644 index 00000000000..45fb720b6c3 --- /dev/null +++ b/libs/datalogger/docs/reference/datalogger/include-timestamp.md @@ -0,0 +1,54 @@ +# include Timestamp + +Set the timestamp format for the data log entries. + +```sig +datalogger.includeTimestamp(FlashLogTimeStampFormat.None) +``` + +A timestamp value is included as one of the data items in a log entry. You can choose which timestamp format you want for your log entries. Time units are from milliseconds to days. If you don't want a timestamp, you use this function to set the timestamp to `none`. If you haven't changed the timestamp format, the default is `milliseconds`. + +## Parameters + +* **format**: the format of the timestamp as time units or `none`: +>* `milliseconds`: time is recorded in milliseconds +>* `seconds`: time is recorded as seconds +>* `minutes`: time is set as minutes +>* `hours`: time is recorded in hours +>* `days`: time is recorded as days +>* `none`: a timestamp is NOT included with a log entry + +## Example + +### Ambient values + +Record the temperature and brightness of light near the @boardname@ every minute. + +```blocks +datalogger.includeTimestamp(FlashLogTimeStampFormat.Minutes) +datalogger.setColumns(["temperature", "light"]) +loops.everyInterval(60000, function () { + datalogger.logData([datalogger.createCV("temperature", input.temperature()), datalogger.createCV("light", input.lightLevel())]) +}) +``` + +### Random buttons + +Randomly record the state of the button presses on the @boardname@ without a timestamp. + +```blocks +datalogger.includeTimestamp(FlashLogTimeStampFormat.None) +datalogger.setColumns(["Button A", "Button B"]) +basic.forever(function () { + datalogger.logData([datalogger.createCV("Button A", input.buttonIsPressed(Button.A)), datalogger.createCV("Button B", input.buttonIsPressed(Button.B))]) + basic.pause(randint(100, 2000)) +}) +``` + +## See also + +[create cv](/reference/datalogger/set-columns) + +```package +datalogger +``` diff --git a/libs/datalogger/docs/reference/datalogger/log-data.md b/libs/datalogger/docs/reference/datalogger/log-data.md new file mode 100644 index 00000000000..4899d11c619 --- /dev/null +++ b/libs/datalogger/docs/reference/datalogger/log-data.md @@ -0,0 +1,33 @@ +# log Data + +Write a data item array to the data log. + +```sig +datalogger.logData(null) +``` + +Data log entries are written to the log as an array of "column" and "value" data items. Each data value you want to record in the log has an column which it belongs to. The data value is attached to it's column by creating a "column-value" object with the data value matched to a column name. + +A log entry is made up of one or more "column-value" objects inserted into an array. This array is sent to the data log to write as the next log entry. + +## Parameters + +* **data**: an array of "column-value" objects to write to the data log. + +## Example + +Record the state of the buttons on the @boardname@ every 500 milliseconds. + +```blocks +loops.everyInterval(500, function () { + datalogger.logData([datalogger.createCV("Button A", input.buttonIsPressed(Button.A)), datalogger.createCV("Button B", input.buttonIsPressed(Button.B))]) +}) +``` + +## See also + +[create cv](/reference/datalogger/create-cv), [set columns](/reference/datalogger/set-columns) + +```package +datalogger +``` \ No newline at end of file diff --git a/libs/datalogger/docs/reference/datalogger/log.md b/libs/datalogger/docs/reference/datalogger/log.md new file mode 100644 index 00000000000..2d235f50b6a --- /dev/null +++ b/libs/datalogger/docs/reference/datalogger/log.md @@ -0,0 +1,33 @@ +# log + +Write a set data item parameters to the data log. + +```sig +datalogger.log(null) +``` + +Data log entries are written to the log as an array of "column" and "value" data items. Each data value you want to record in the log has an column which it belongs to. The data value is attached to it's column by creating a "column-value" object with the data value matched to a column name. + +A log entry is made up of one or more "column-value" objects inserted into an array. This array is sent to the data log to write as the next log entry. + +## Parameters + +* **data1 - data10**: one or more (up to 10) of "column-value" objects to write to the data log. + +## Example + +Record the state of the buttons on the @boardname@ every 500 milliseconds. + +```blocks +loops.everyInterval(500, function () { + datalogger.log(datalogger.createCV("Button A", input.buttonIsPressed(Button.A)), datalogger.createCV("Button B", input.buttonIsPressed(Button.B))) +}) +``` + +## See also + +[create cv](/reference/datalogger/create-cv), [set column titles](/reference/datalogger/set-column-titles) + +```package +datalogger +``` \ No newline at end of file diff --git a/libs/datalogger/docs/reference/datalogger/mirror-to-serial.md b/libs/datalogger/docs/reference/datalogger/mirror-to-serial.md new file mode 100644 index 00000000000..b34747df4a0 --- /dev/null +++ b/libs/datalogger/docs/reference/datalogger/mirror-to-serial.md @@ -0,0 +1,29 @@ +# mirror To Serial + +Write a copy of the logged data items to the serial output also. + +```sig +datalogger.mirrorToSerial(false) +``` + +By default, the same data that is written to the data log is also sent to the serial output port. You can choose to not send the data to serial if you only want it logged in the @boardname@ flash memory. + +## Parameters + +* **on**: a [boolean](/types/boolean) that is set to `true` to write a copy of the data log items to the serial output. Otherwise, use `false` to just write to the data items to the log only. + +## Example + +Record the temperature and light level every minute by logging both input values. Send the data only to the data log. + +```blocks +datalogger.mirrorToSerial(false) +datalogger.setColumns(["temperature", "light"]) +loops.everyInterval(60000, function () { + datalogger.logData([datalogger.createCV("temperature", input.temperature()), datalogger.createCV("light", input.lightLevel())]) +}) +``` + +```package +datalogger +``` \ No newline at end of file diff --git a/libs/datalogger/docs/reference/datalogger/on-log-full.md b/libs/datalogger/docs/reference/datalogger/on-log-full.md new file mode 100644 index 00000000000..a4dd8dbe347 --- /dev/null +++ b/libs/datalogger/docs/reference/datalogger/on-log-full.md @@ -0,0 +1,34 @@ +# on Log Full + +Run code in an event when the data log is full. + +```sig +datalogger.onLogFull(function() {}) +``` + +## Example + +Notify the user when the data log is full. Wait 2 seconds to let them clear the log by pressing the **B** button. + +```blocks +datalogger.onLogFull(function () { + basic.showString("Data Log is FULL! Press B to clear") + for (let index = 0; index < 20; index++) { + if (input.buttonIsPressed(Button.B)) { + datalogger.deleteLog() + break; + } else { + basic.pause(100) + } + } + basic.clearScreen() +}) +``` + +## See also + +[delete log](/reference/datalogger/delete-log) + +```package +datalogger +``` \ No newline at end of file diff --git a/libs/datalogger/docs/reference/datalogger/set-column-titles.md b/libs/datalogger/docs/reference/datalogger/set-column-titles.md new file mode 100644 index 00000000000..d9b6f859a4c --- /dev/null +++ b/libs/datalogger/docs/reference/datalogger/set-column-titles.md @@ -0,0 +1,35 @@ +# set Column Titles + +Set the names and order of the columns in the data log. + +```sig +datalogger.setColumnTitles("") +``` + +The first entry in the data log is the "header" row which names the columns of the data values recorded. By default, the order in which the columns appear depends on the order of the column-value items appearing in the first entry written to the data log containing those items. + +You can set the names and the order of the columns for the log so that data values appear in the positions that you want them to be in. This also helps keep column order when data items happen to be in different places in log entry column-value array. + +## Parameters + +* **col1 - col10 **: one or more (up to 10) [strings](/types/string) that are the column names in the order they will appear in the data log header. + +## Example + +Set the columns for the data log to `value1`, `value2`, and `value3`. Make 3 log entries with the data items set in a different order. Check the logged values to see that they all appear in the correct colunms. + +```blocks +datalogger.includeTimestamp(FlashLogTimeStampFormat.None) +datalogger.setColumnTitles("value1", "value2", "value3") +datalogger.logData([datalogger.createCV("value2", 9873), datalogger.createCV("value1", 23987), datalogger.createCV("value3", 5789)]) +datalogger.logData([datalogger.createCV("value3", 567), datalogger.createCV("value2", 789), datalogger.createCV("value1", 62)]) +datalogger.logData([datalogger.createCV("value1", 0), datalogger.createCV("value3", 87), datalogger.createCV("value2", 8)]) +``` + +## See also + +[create cv](/reference/datalogger/create-cv), [include timestamp](/reference/datalogger/include-timestamp) + +```package +datalogger +``` diff --git a/libs/datalogger/docs/reference/datalogger/set-columns.md b/libs/datalogger/docs/reference/datalogger/set-columns.md new file mode 100644 index 00000000000..a00bff18fc5 --- /dev/null +++ b/libs/datalogger/docs/reference/datalogger/set-columns.md @@ -0,0 +1,35 @@ +# set Columns + +Set the names and order of the columns in the data log. + +```sig +datalogger.setColumns(null) +``` + +The first entry in the data log is the "header" row which names the columns of the data values recorded. By default, the order in which the columns appear depends on the order of the column-value items appearing in the first entry written to the data log containing those items. + +You can set the names and the order of the columns for the log so that data values appear in the positions that you want them to be in. This also helps keep column order when data items happen to be in different places in log entry column-value array. + +## Parameters + +* **cols**: a [string](/types/string) array containing the column names in the order they will appear in the data log header. + +## Example + +Set the columns for the data log to `value1`, `value2`, and `value3`. Make 3 log entries with the data items set in a different order. Check the logged values to see that they all appear in the correct colunms. + +```blocks +datalogger.includeTimestamp(FlashLogTimeStampFormat.None) +datalogger.setColumns(["value1", "value2", "value3"]) +datalogger.logData([datalogger.createCV("value2", 9873), datalogger.createCV("value1", 23987), datalogger.createCV("value3", 5789)]) +datalogger.logData([datalogger.createCV("value3", 567), datalogger.createCV("value2", 789), datalogger.createCV("value1", 62)]) +datalogger.logData([datalogger.createCV("value1", 0), datalogger.createCV("value3", 87), datalogger.createCV("value2", 8)]) +``` + +## See also + +[create cv](/reference/datalogger/create-cv), [include timestamp](/reference/datalogger/include-timestamp) + +```package +datalogger +``` diff --git a/libs/datalogger/pxt.json b/libs/datalogger/pxt.json new file mode 100644 index 00000000000..b42faa0081d --- /dev/null +++ b/libs/datalogger/pxt.json @@ -0,0 +1,21 @@ +{ + "name": "datalogger", + "description": "Data logging to flash memory. micro:bit (V2) only.", + "files": [ + "datalogger.ts" + ], + "public": true, + "disablesVariants": [ + "mbdal" + ], + "dependencies": { + "core": "file:../core", + "flashlog": "file:../flashlog" + }, + "yotta": { + "config": { + "MICROBIT_BLE_UTILITY_SERVICE": 1, + "MICROBIT_BLE_UTILITY_SERVICE_PAIRING": 1 + } + } +} \ No newline at end of file diff --git a/libs/flashlog/README.md b/libs/flashlog/README.md new file mode 100644 index 00000000000..8b667253545 --- /dev/null +++ b/libs/flashlog/README.md @@ -0,0 +1 @@ +# datalog diff --git a/libs/flashlog/_locales/flashlog-jsdoc-strings.json b/libs/flashlog/_locales/flashlog-jsdoc-strings.json new file mode 100644 index 00000000000..33b9cecaa55 --- /dev/null +++ b/libs/flashlog/_locales/flashlog-jsdoc-strings.json @@ -0,0 +1,15 @@ +{ + "flashlog": "Storing structured data in flash.", + "flashlog.beginRow": "Creates a new row in the log, ready to be populated by logData()", + "flashlog.clear": "Resets all data stored in persistent storage.", + "flashlog.endRow": "Complete a row in the log, and pushes to persistent storage.", + "flashlog.getNumberOfRows": "Number of rows currently used by the datalogger, start counting at fromRowIndex\nTreats the header as the first row\n\n@returns header + rows", + "flashlog.getNumberOfRows|param|fromRowIndex": "0-based index of start: Default value of 0", + "flashlog.getRows": "Get all rows separated by a newline & each column separated by a comma.\nStarting at the 0-based index fromRowIndex & counting inclusively until nRows.\n\n\n@returns String where newlines denote rows & commas denote columns", + "flashlog.getRows|param|fromRowIndex": "0-based index of start", + "flashlog.getRows|param|nRows": "inclusive count from fromRowIndex", + "flashlog.logData": "Populates the current row with the given key/value pair.", + "flashlog.logString": "Inject the given row into the log as text, ignoring key/value pairs.", + "flashlog.setSerialMirroring": "Defines if data logging should also be streamed over the serial port.\n* @param enable True to enable serial port streaming, false to disable.", + "flashlog.setTimeStamp": "Determines the format of the timestamp data to be added (if any).\nIf requested, time stamps will be automatically added to each row of data\nas an integer value rounded down to the unit specified.\n* @param format The format of timestamp to use." +} \ No newline at end of file diff --git a/libs/flashlog/_locales/flashlog-strings.json b/libs/flashlog/_locales/flashlog-strings.json new file mode 100644 index 00000000000..84a82572d64 --- /dev/null +++ b/libs/flashlog/_locales/flashlog-strings.json @@ -0,0 +1,11 @@ +{ + "FlashLogTimeStampFormat.Days|block": "days", + "FlashLogTimeStampFormat.Hours|block": "hours", + "FlashLogTimeStampFormat.Milliseconds|block": "milliseconds", + "FlashLogTimeStampFormat.Minutes|block": "minutes", + "FlashLogTimeStampFormat.None|block": "none", + "FlashLogTimeStampFormat.Seconds|block": "seconds", + "flashlog|block": "flashlog", + "{id:category}Flashlog": "Flashlog", + "{id:group}micro:bit (V2)": "micro:bit (V2)" +} \ No newline at end of file diff --git a/libs/flashlog/docs/reference/flashlog.md b/libs/flashlog/docs/reference/flashlog.md new file mode 100644 index 00000000000..e0bcdd3c8c5 --- /dev/null +++ b/libs/flashlog/docs/reference/flashlog.md @@ -0,0 +1,41 @@ +# Flash log + +The flash log extension logs user data to the flash storage on the @boardname@. + +### ~ reminder + +#### Works with micro:bit V2 + +![works with micro:bit V2 only image](/static/v2/v2-only.png) + +Using these blocks requires the [micro:bit V2](/device/v2) hardware. If you use any blocks that attempt access flash memory on a micro:bit v1 board, you will see the **927** error code on the screen. + +### ~ + +A data item is either name/value pair or a string value. A name/value pair has a value name and a data value. A [string](/types/string) value is just regular text. + +## Blocks in this extension + +```cards +flashlog.beginRow() +flashlog.endRow() +flashlog.logData("", "") +flashlog.logString("") +flashlog.setTimeStamp(FlashLogTimeStampFormat.None) +flashlog.clear() +flashlog.setSerialMirroring(false) +``` + +## See also + +[begin row](/reference/flashlog/begin-row), +[end row](/reference/flashlog/set-column-titles), +[log data](/reference/flashlog/log-data), +[log string](/reference/flashlog/log-string), +[set time stamp](/reference/flashlog/include-timestamp), +[clear](/reference/flashlog/clear), +[set serial mirroring](/reference/flashlog/set-serial-mirroring) + +```package +flashlog +``` diff --git a/libs/flashlog/docs/reference/flashlog/begin-row.md b/libs/flashlog/docs/reference/flashlog/begin-row.md new file mode 100644 index 00000000000..6b646209cb7 --- /dev/null +++ b/libs/flashlog/docs/reference/flashlog/begin-row.md @@ -0,0 +1,37 @@ +# begin Row + +Initialize a new row for adding log data. + +```sig +flashlog.beginRow() +``` + +Beginning a row resets the data value count and allocates temporary memory for the row's logged data values. + +**Note**: Using a ``||flashlog:begin row||`` before using an ``||flashlog:end row||`` on the current row will **end** the current row and write it to the flash log before beginning the new row. + +## Returns + +* returns **DEVICE_OK** if successful. Otherwise, some other device condition is returned. + +## Example + +Record the temperature and light level every minute by logging both input values. Send the data only to the data log. + +```blocks +flashlog.setSerialMirroring(false) +loops.everyInterval(60000, function () { + flashlog.beginRow() + flashlog.logData("temperature", input.temperature()) + flashlog.logData("light", input.lightLevel()) + flashlog.endRow() +}) +``` + +## See also + +[end row](/reference/flashlog/end-row) + +```package +flashlog +``` \ No newline at end of file diff --git a/libs/flashlog/docs/reference/flashlog/clear.md b/libs/flashlog/docs/reference/flashlog/clear.md new file mode 100644 index 00000000000..98100f0987a --- /dev/null +++ b/libs/flashlog/docs/reference/flashlog/clear.md @@ -0,0 +1,32 @@ +# clear + +Clear the contents of log data in flash storage. + +```sig +flashlog.clear(true) +``` + +The contents of the flash log are cleared by resetting the log's current position to the beginning to overwrite old data. Also, you can reset all of the data in the log to zeros (0x00000000). Resetting the log's data position is much faster than clearing the entire flash log storage. + +## Parameters + +* **fullErase**: a [boolean](/types/boolean) that if set to `true` will reset the entire log storage. Otherwise, use `false` to reset the log data position and overwrite older logged data. + +## Example + +Clear out the last logged data from the flash log. Start a new log to record the temperature and light level every minute by logging both input values. + +```blocks +flashlog.clear(false) +flashlog.setSerialMirroring(false) +loops.everyInterval(60000, function () { + flashlog.beginRow() + flashlog.logData("temperature", input.temperature()) + flashlog.logData("light", input.lightLevel()) + flashlog.endRow() +}) +``` + +```package +flashlog +``` \ No newline at end of file diff --git a/libs/flashlog/docs/reference/flashlog/end-row.md b/libs/flashlog/docs/reference/flashlog/end-row.md new file mode 100644 index 00000000000..4fdb94cc60e --- /dev/null +++ b/libs/flashlog/docs/reference/flashlog/end-row.md @@ -0,0 +1,35 @@ +# end Row + +Finish formatting a log row and write it to flash storage. + +```sig +flashlog.endRow() +``` + +Ending a row assembles the logged data values, adds the timestamp if selected, and writes the complete formatted row to flash storage. + +## Returns + +* returns **DEVICE_OK** if successful. Otherwise, some other device condition is returned. + +## Example + +Record the temperature and light level every minute by logging both input values. Send the data only to the data log. + +```blocks +flashlog.setSerialMirroring(false) +loops.everyInterval(60000, function () { + flashlog.beginRow() + flashlog.logData("temperature", input.temperature()) + flashlog.logData("light", input.lightLevel()) + flashlog.endRow() +}) +``` + +## See also + +[begin row](/reference/flashlog/begin-row) + +```package +flashlog +``` \ No newline at end of file diff --git a/libs/flashlog/docs/reference/flashlog/log-data.md b/libs/flashlog/docs/reference/flashlog/log-data.md new file mode 100644 index 00000000000..2f6ff72df70 --- /dev/null +++ b/libs/flashlog/docs/reference/flashlog/log-data.md @@ -0,0 +1,36 @@ +# log Data + +Add a data value to the current log row. + +```sig +flashlog.logData("", "") +``` + +Data values are added to a row as name/value pairs. These pairs are a combination of a name (key) as the first parameter and the value as the second parameter. The value parameter is represented as [string](/types/string) when added to the log. + +## Parameters + +* **key**: a [string](/types/string) that is the value's name, like "temperature" or "acceleration". +* **value**: The value of the data to log with the **key** name. This any single value data such as a [number](/types/number), [boolean](/types/boolean), or a [string](/types/string). + +## Example + +Record the temperature and light level every minute by logging both input values. Send the data only to the data log. + +```blocks +flashlog.setSerialMirroring(false) +loops.everyInterval(60000, function () { + flashlog.beginRow() + flashlog.logData("temperature", input.temperature()) + flashlog.logData("light", input.lightLevel()) + flashlog.endRow() +}) +``` + +## See also + +[log string](/reference/flashlog/log-string) + +```package +flashlog +```. \ No newline at end of file diff --git a/libs/flashlog/docs/reference/flashlog/log-string.md b/libs/flashlog/docs/reference/flashlog/log-string.md new file mode 100644 index 00000000000..453bbd1418c --- /dev/null +++ b/libs/flashlog/docs/reference/flashlog/log-string.md @@ -0,0 +1,49 @@ +# log String + +Add a string into the current log row. + +```sig +flashlog.logString("") +``` + +You can log a string value into the a log row. It is inserted into the row with other data items (name/value pairs) and strings. + +## Parameters + +* **value**: a [string](/types/value) that is added to the current log row. + +## Returns + +* returns **DEVICE_OK** if successful. Otherwise, some other device condition is returned. + +## Example + +See if the accelerometer detects any shaking and record a comment to the log every second. + +```blocks +let shakey = 0 +flashlog.setSerialMirroring(false) +flashlog.seTimeStamp(FlashLogTimeStampFormat.Seconds) +loops.everyInterval(1000, function () { + flashlog.beginRow() + shakey = input.acceleration(Dimension.Strength) + if (shakey < 256) { + flashlog.logString("It's seems calm this second.") + } + else if (shakey < 684) { + flashlog.logString("It's a somewhat unstable.") + } + else { + flashlog.logString("It's really shaking!") + } + flashlog.endRow() +}) +``` + +## See also + +[log data](/reference/flashlog/log-data) + +```package +flashlog +``` \ No newline at end of file diff --git a/libs/flashlog/docs/reference/flashlog/set-serial-mirroring.md b/libs/flashlog/docs/reference/flashlog/set-serial-mirroring.md new file mode 100644 index 00000000000..6f4ea6467e9 --- /dev/null +++ b/libs/flashlog/docs/reference/flashlog/set-serial-mirroring.md @@ -0,0 +1,31 @@ +# set Serial Mirroring + +Write a copy of the logged data items to the serial output also. + +```sig +flashlog.setSerialMirroring(false) +``` + +By default, the same data that is written to the flash log is also sent to the serial output port. You can choose to not send the data to serial if you only want it logged in the @boardname@ flash memory. + +## Parameters + +* **enable**: a [boolean](/types/boolean) that is set to `true` to write a copy of the data log items to the serial output. Otherwise, use `false` to just write to the data items to the log only. + +## Example + +Record the temperature and light level every minute by logging both input values. Send the data only to the data log. + +```blocks +flashlog.setSerialMirroring(false) +loops.everyInterval(60000, function () { + flashlog.beginRow() + flashlog.logData("temperature", input.temperature()) + flashlog.logData("light", input.lightLevel()) + flashlog.endRow() +}) +``` + +```package +flashlog +``` \ No newline at end of file diff --git a/libs/flashlog/docs/reference/flashlog/set-time-stamp.md b/libs/flashlog/docs/reference/flashlog/set-time-stamp.md new file mode 100644 index 00000000000..60c223c1359 --- /dev/null +++ b/libs/flashlog/docs/reference/flashlog/set-time-stamp.md @@ -0,0 +1,42 @@ +# set Time Stamp + +Enable and set the time units for the flash log timestamp. + +```sig +flashlog.seTimeStamp(FlashLogTimeStampFormat.None) +``` + +If you want a timestamp added to the rows in the log, then select the uint of time to use. Otherwise, if the value for time **format** is `none`, no timestamp is added to the logged row data. + +## Parameters + +* **format**: the unit of time to use for the timestamp added to the logged row data: +>* `milliseconds`: time is recorded in milliseconds +>* `seconds`: time is recorded as seconds +>* `minutes`: time is set as minutes +>* `hours`: time is recorded in hours +>* `days`: time is recorded as days +>* `none`: a timestamp is NOT included with a log entry + +## Example + +Record the temperature and light level every minute by logging both input values. Use a timestamp of `minutes`. + +```blocks +flashlog.setSerialMirroring(false) +flashlog.seTimeStamp(FlashLogTimeStampFormat.Minutes) +loops.everyInterval(60000, function () { + flashlog.beginRow() + flashlog.logData("temperature", input.temperature()) + flashlog.logData("light", input.lightLevel()) + flashlog.endRow() +}) +``` + +## See also + +[end row](/reference/flashlog/end-row) + +```package +flashlog +``` \ No newline at end of file diff --git a/libs/flashlog/enums.d.ts b/libs/flashlog/enums.d.ts new file mode 100644 index 00000000000..bcffffd2341 --- /dev/null +++ b/libs/flashlog/enums.d.ts @@ -0,0 +1,22 @@ +// Auto-generated. Do not edit. + + + declare const enum FlashLogTimeStampFormat + { + //% block="none" + None = 0, + //% block="milliseconds" + Milliseconds = 1, + //% block="seconds" + Seconds = 10, + //% block="minutes" + Minutes = 600, + //% block="hours" + Hours = 36000, + //% block="days" + Days = 864000, + } +declare namespace flashlog { +} + +// Auto-generated. Do not edit. Really. diff --git a/libs/flashlog/flashlog.cpp b/libs/flashlog/flashlog.cpp new file mode 100644 index 00000000000..88979d319e1 --- /dev/null +++ b/libs/flashlog/flashlog.cpp @@ -0,0 +1,169 @@ +#include "pxt.h" + +#if MICROBIT_CODAL +#include "MicroBitLog.h" +#endif + +enum class FlashLogTimeStampFormat +{ + //% block="none" + None = 0, + //% block="milliseconds" + Milliseconds = 1, + //% block="seconds" + Seconds = 10, + //% block="minutes" + Minutes = 600, + //% block="hours" + Hours = 36000, + //% block="days" + Days = 864000 +}; + +/** + * Storing structured data in flash. + */ +//% +namespace flashlog { + +/** +* Creates a new row in the log, ready to be populated by logData() +**/ +//% help=flashlog/begin-row +//% parts="flashlog" +//% blockGap=8 +//% group="micro:bit (V2)" +int beginRow() { +#if MICROBIT_CODAL + return uBit.log.beginRow(); +#else + return DEVICE_NOT_SUPPORTED; +#endif +} + +/** +* Populates the current row with the given key/value pair. +**/ +//% help=flashlog/log-data +//% parts="flashlog" +//% blockGap=8 +//% group="micro:bit (V2)" +int logData(String key, String value) { + if (NULL == key || NULL == value) + return DEVICE_INVALID_PARAMETER; +#if MICROBIT_CODAL + return uBit.log.logData(MSTR(key), MSTR(value)); +#else + return DEVICE_NOT_SUPPORTED; +#endif +} + +/** +* Inject the given row into the log as text, ignoring key/value pairs. +**/ +//% help=flashlog/log-string +//% parts="flashlog" +//% blockGap=8 +//% group="micro:bit (V2)" +int logString(String value) { + if (NULL == value) + return DEVICE_INVALID_PARAMETER; +#if MICROBIT_CODAL + return uBit.log.logString(MSTR(value)); +#else + return DEVICE_NOT_SUPPORTED; +#endif +} + +/** +* Complete a row in the log, and pushes to persistent storage. +**/ +//% help=flashlog/end-row +//% parts="flashlog" +//% blockGap=8 +//% group="micro:bit (V2)" +int endRow() { +#if MICROBIT_CODAL + return uBit.log.endRow(); +#else + return DEVICE_NOT_SUPPORTED; +#endif +} + +/** +* Resets all data stored in persistent storage. +**/ +//% help=flashlog/clear +//% parts="flashlog" +//% blockGap=8 +//% group="micro:bit (V2)" +void clear(bool fullErase) { +#if MICROBIT_CODAL + uBit.log.clear(fullErase); +#endif +} + +/** +* Determines the format of the timestamp data to be added (if any). +* If requested, time stamps will be automatically added to each row of data +* as an integer value rounded down to the unit specified. +* +* @param format The format of timestamp to use. +*/ +//% help=flashlog/set-time-stamp +//% parts="flashlog" +//% blockGap=8 +//% group="micro:bit (V2)" +void setTimeStamp(FlashLogTimeStampFormat format) { +#if MICROBIT_CODAL + return uBit.log.setTimeStamp((codal::TimeStampFormat)format); +#endif +} + +/** + * Defines if data logging should also be streamed over the serial port. + * + * @param enable True to enable serial port streaming, false to disable. +*/ +//% help=flashlog/set-serial-mirroring +//% parts="flashlog" +//% blockGap=8 +//% group="micro:bit (V2)" +void setSerialMirroring(bool enable) { +#if MICROBIT_CODAL + return uBit.log.setSerialMirroring(enable); +#endif +} + +/** +* Number of rows currently used by the datalogger, start counting at fromRowIndex +* Treats the header as the first row +* @param fromRowIndex 0-based index of start: Default value of 0 +* @returns header + rows +*/ +//% +int getNumberOfRows(int fromRowIndex = 0) { +#if MICROBIT_CODAL + return uBit.log.getNumberOfRows(fromRowIndex); +#else + return DEVICE_NOT_SUPPORTED; +#endif +} + +/** +* Get all rows separated by a newline & each column separated by a comma. +* Starting at the 0-based index fromRowIndex & counting inclusively until nRows. +* @param fromRowIndex 0-based index of start +* @param nRows inclusive count from fromRowIndex +* @returns String where newlines denote rows & commas denote columns +*/ +//% +String getRows(int fromRowIndex, int nRows) { +#if MICROBIT_CODAL + return PSTR(uBit.log.getRows(fromRowIndex, nRows)); +#else + return DEVICE_NOT_SUPPORTED; +#endif +} + +} diff --git a/libs/flashlog/pxt.json b/libs/flashlog/pxt.json new file mode 100644 index 00000000000..76e8a88887b --- /dev/null +++ b/libs/flashlog/pxt.json @@ -0,0 +1,21 @@ +{ + "name": "flashlog", + "description": "Data logging to flash.", + "files": [ + "README.md", + "flashlog.cpp", + "shims.d.ts", + "enums.d.ts" + ], + "testFiles": [ + "test.ts" + ], + "searchOnly": true, + "public": true, + "disablesVariants": [ + "mbdal" + ], + "dependencies": { + "core": "file:../core" + } +} \ No newline at end of file diff --git a/libs/flashlog/shims.d.ts b/libs/flashlog/shims.d.ts new file mode 100644 index 00000000000..5fa820f5ea2 --- /dev/null +++ b/libs/flashlog/shims.d.ts @@ -0,0 +1,99 @@ +// Auto-generated. Do not edit. + + + /** + * Storing structured data in flash. + */ + //% +declare namespace flashlog { + + /** + * Creates a new row in the log, ready to be populated by logData() + **/ + //% help=flashlog/begin-row + //% parts="flashlog" + //% blockGap=8 + //% group="micro:bit (V2)" shim=flashlog::beginRow + function beginRow(): int32; + + /** + * Populates the current row with the given key/value pair. + **/ + //% help=flashlog/log-data + //% parts="flashlog" + //% blockGap=8 + //% group="micro:bit (V2)" shim=flashlog::logData + function logData(key: string, value: string): int32; + + /** + * Inject the given row into the log as text, ignoring key/value pairs. + **/ + //% help=flashlog/log-string + //% parts="flashlog" + //% blockGap=8 + //% group="micro:bit (V2)" shim=flashlog::logString + function logString(value: string): int32; + + /** + * Complete a row in the log, and pushes to persistent storage. + **/ + //% help=flashlog/end-row + //% parts="flashlog" + //% blockGap=8 + //% group="micro:bit (V2)" shim=flashlog::endRow + function endRow(): int32; + + /** + * Resets all data stored in persistent storage. + **/ + //% help=flashlog/clear + //% parts="flashlog" + //% blockGap=8 + //% group="micro:bit (V2)" shim=flashlog::clear + function clear(fullErase: boolean): void; + + /** + * Determines the format of the timestamp data to be added (if any). + * If requested, time stamps will be automatically added to each row of data + * as an integer value rounded down to the unit specified. + * + * @param format The format of timestamp to use. + */ + //% help=flashlog/set-time-stamp + //% parts="flashlog" + //% blockGap=8 + //% group="micro:bit (V2)" shim=flashlog::setTimeStamp + function setTimeStamp(format: FlashLogTimeStampFormat): void; + + /** + * Defines if data logging should also be streamed over the serial port. + * + * @param enable True to enable serial port streaming, false to disable. + */ + //% help=flashlog/set-serial-mirroring + //% parts="flashlog" + //% blockGap=8 + //% group="micro:bit (V2)" shim=flashlog::setSerialMirroring + function setSerialMirroring(enable: boolean): void; + + /** + * Number of rows currently used by the datalogger, start counting at fromRowIndex + * Treats the header as the first row + * @param fromRowIndex 0-based index of start: Default value of 0 + * @returns header + rows + */ + //% fromRowIndex.defl=0 shim=flashlog::getNumberOfRows + function getNumberOfRows(fromRowIndex?: int32): int32; + + /** + * Get all rows separated by a newline & each column separated by a comma. + * Starting at the 0-based index fromRowIndex & counting inclusively until nRows. + * @param fromRowIndex 0-based index of start + * @param nRows inclusive count from fromRowIndex + * @returns String where newlines denote rows & commas denote columns + */ + //% shim=flashlog::getRows + function getRows(fromRowIndex: int32, nRows: int32): string; +} + +// Auto-generated. Do not edit. Really. diff --git a/libs/flashlog/test.ts b/libs/flashlog/test.ts new file mode 100644 index 00000000000..0ee38e6e487 --- /dev/null +++ b/libs/flashlog/test.ts @@ -0,0 +1,12 @@ +input.onButtonPressed(Button.AB, function() { + flashlog.clear() +}) +flashlog.setTimeStamp(FlashLogTimeStampFormat.Milliseconds) +basic.forever(function () { + led.toggle(0, 0) + const ax = input.acceleration(Dimension.X) + flashlog.beginRow() + flashlog.logData(`a.x`, ax) + flashlog.logData(`a.y`, input.acceleration(Dimension.Y)) + flashlog.endRow() +}) diff --git a/libs/fonts/_locales/fonts-jsdoc-strings.json b/libs/fonts/_locales/fonts-jsdoc-strings.json new file mode 100644 index 00000000000..9e26dfeeb6e --- /dev/null +++ b/libs/fonts/_locales/fonts-jsdoc-strings.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/libs/fonts/_locales/fonts-strings.json b/libs/fonts/_locales/fonts-strings.json new file mode 100644 index 00000000000..9e26dfeeb6e --- /dev/null +++ b/libs/fonts/_locales/fonts-strings.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/libs/fonts/font12.jres b/libs/fonts/font12.jres new file mode 100644 index 00000000000..94c2c27c008 --- /dev/null +++ b/libs/fonts/font12.jres @@ -0,0 +1,6 @@ +{ + "bitmaps.font12": { + "mimeType": "font/x-mkcd-b26", + "data": "IAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhAAAAAAAABvwAAAAAAAAAAAAAAAAAAAAAACIAAAAcAAAAAAAcAAAAAAAAAAAAAAAAAAAAIwCgAPgHoAD4B6AAAAAAAAAAAAAAAAAAAAAkAAAAGAIkBEYMiAMAAAAAAAAAAAAAAAAAACUAOALEAXgAIAOQBAgDAAAAAAAAAAAAAAAAJgCAA3gEpAQYA4ADQAQAAAAAAAAAAAAAAAAnAAAAAAAcAAAAAAAAAAAAAAAAAAAAAAAAACgAAAAAAPgBBgYBCAAAAAAAAAAAAAAAAAAAKQAAAAEIBgb4AQAAAAAAAAAAAAAAAAAAAAAqAAQAFAAOABQABAAAAAAAAAAAAAAAAAAAACsAQABAAPgDQABAAAAAAAAAAAAAAAAAAAAALAAAAAAAAAkABgAAAAAAAAAAAAAAAAAAAAAtAAAAgACAAIAAAAAAAAAAAAAAAAAAAAAAAC4AAAAAAAAGAAAAAAAAAAAAAAAAAAAAAAAALwAADIADYAAcAAMAAAAAAAAAAAAAAAAAAAAwAAAA+AMEBAQE+AMAAAAAAAAAAAAAAAAAADEAAAAEBAQE/AcABAAEAAAAAAAAAAAAAAAAMgAAAAgGBAXEBDgEAAAAAAAAAAAAAAAAAAAzAAAACAJEBEQEuAMAAAAAAAAAAAAAAAAAADQAAACAAXABCAH8BwABAAAAAAAAAAAAAAAANQAAAjwEJAQkBMQDAAAAAAAAAAAAAAAAAAA2AAAA8ANIBEQEiAMAAAAAAAAAAAAAAAAAADcAAAAEAMQHNAAMAAAAAAAAAAAAAAAAAAAAOAAAALgDRAREBLgDAAAAAAAAAAAAAAAAAAA5AAAAeAKEBIgC8AEAAAAAAAAAAAAAAAAAADoAAAAAADAGAAAAAAAAAAAAAAAAAAAAAAAAOwAAAAAAGAkABgAAAAAAAAAAAAAAAAAAAAA8AEAAoACgAKAAEAEAAAAAAAAAAAAAAAAAAD0AIAEgASABIAEgAQAAAAAAAAAAAAAAAAAAPgAAABABoACgAKAAQAAAAAAAAAAAAAAAAAA/AAAACACEBmQAGAAAAAAAAAAAAAAAAAAAAEAA8AMMBMIIIgkkCfgJAAAAAAAAAAAAAAAAQQAAB/gAhAD4AAAHAAAAAAAAAAAAAAAAAABCAAAA/AdEBEQEuAMAAAAAAAAAAAAAAAAAAEMAAADwAQgCBAQEBAgCAAAAAAAAAAAAAAAARAAAAPwHBAQIAvABAAAAAAAAAAAAAAAAAABFAAAA/AdEBEQERAQAAAAAAAAAAAAAAAAAAEYAAAD8B0QARABEAAAAAAAAAAAAAAAAAAAARwAAAPABCAJEBMgDAAAAAAAAAAAAAAAAAABIAAAA/AdAAEAA/AcAAAAAAAAAAAAAAAAAAEkAAAAEBPwHBAQEBAAAAAAAAAAAAAAAAAAASgAAAAACAAQABPwDAAAAAAAAAAAAAAAAAABLAAAA/AdAAPAADAMEBAAAAAAAAAAAAAAAAEwAAAD8BwAEAAQABAAAAAAAAAAAAAAAAAAATQAAAPwHOABwAPwHAAAAAAAAAAAAAAAAAABOAAAA/Ac4AMAD/AcAAAAAAAAAAAAAAAAAAE8AAAD4AwQEBAT4AwAAAAAAAAAAAAAAAAAAUAAAAPwHhACEAEQAeAAAAAAAAAAAAAAAAABRAAAA/AECAgIG/AkAAAAAAAAAAAAAAAAAAFIAAAD8B0QAxAE4BgAAAAAAAAAAAAAAAAAAUwAAABgCJAREBIgDAAAAAAAAAAAAAAAAAABUAAAABAAEAPwHBAAEAAAAAAAAAAAAAAAAAFUAAAD8AwAEAAT8AwAAAAAAAAAAAAAAAAAAVgAMAPADAATwAwwAAAAAAAAAAAAAAAAAAABXAHwAgAfgAfABAAf8AAAAAAAAAAAAAAAAAFgABAS4A0AAuAMEBAAAAAAAAAAAAAAAAAAAWQAEADgAwAcwAAwAAAAAAAAAAAAAAAAAAABaAAQEBAfkBBwEBAQAAAAAAAAAAAAAAAAAAFsAAAAAAP8PAQgBCAAAAAAAAAAAAAAAAAAAXAADABwAYACAAwAMAAAAAAAAAAAAAAAAAABdAAAAAQgBCP8PAAAAAAAAAAAAAAAAAAAAAF4AQAA4AAQAOABAAAAAAAAAAAAAAAAAAAAAXwAACAAIAAgACAAIAAAAAAAAAAAAAAAAAABgAAAAAAACAAQAAAAAAAAAAAAAAAAAAAAAAGEAAAAgA5AEkATgBwAAAAAAAAAAAAAAAAAAYgAAAPwHEAQQBOADAAAAAAAAAAAAAAAAAABjAAAAwAEgAhAEMAQAAgAAAAAAAAAAAAAAAGQAAADgAxAEEAT8BwAAAAAAAAAAAAAAAAAAZQAAAOADkASQBOAEAAAAAAAAAAAAAAAAAABmAAAAEAD4BxQAFAAAAAAAAAAAAAAAAAAAAGcAAAC4BkQJRAk8CQQGAAAAAAAAAAAAAAAAaAAAAPwHEAAQAOAHAAAAAAAAAAAAAAAAAABpAAAAAAD0BwAAAAAAAAAAAAAAAAAAAAAAAGoAAAgACAAI/QcAAAAAAAAAAAAAAAAAAAAAawAAAP4HgADgARAGEAQAAAAAAAAAAAAAAABsAAAAAAD8AwAEAAQAAAAAAAAAAAAAAAAAAG0A8AcQABAA4AcQAOAHAAAAAAAAAAAAAAAAbgAAAPAHEAAQAOAHAAAAAAAAAAAAAAAAAABvAAAA4AMQBBAE4AMAAAAAAAAAAAAAAAAAAHAAAAD8DwQBBAH4AAAAAAAAAAAAAAAAAAAAcQAAAPgABAEEAfwPAAAAAAAAAAAAAAAAAAByAAAA8AcgABAAEAAAAAAAAAAAAAAAAAAAAHMAAABgApAEkAQgAwAAAAAAAAAAAAAAAAAAdAAAABAA/AMQBBAEAAAAAAAAAAAAAAAAAAB1AAAA8AMABAAE8AcAAAAAAAAAAAAAAAAAAHYAMADAAwAEwAMwAAAAAAAAAAAAAAAAAAAAdwDwAAAH4APAAQAH8AAAAAAAAAAAAAAAAAB4ABAEYAOAAGADEAQAAAAAAAAAAAAAAAAAAHkADAhwCIAH8AAMAAAAAAAAAAAAAAAAAAAAegAAAAAEEAeQBHAEEAQAAAAAAAAAAAAAAAB7AAAAQAC+BwEIAQgAAAAAAAAAAAAAAAAAAHwAAAAAAP8PAAAAAAAAAAAAAAAAAAAAAAAAfQAAAAEIAQi+B0AAAAAAAAAAAAAAAAAAAAB+AIAAQABAAIAAgABAAAAAAAAAAAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAoQAAANgPAAAAAAAAAAAAAAAAAAAAAAAAAACiAAAA4AEQAvgHEAIQAQAAAAAAAAAAAAAAAKMAAABABvgFRAREBAgEAAAAAAAAAAAAAAAApAAAAPABEAEQARAB6AEAAAAAAAAAAAAAAAClAEQBeAHAB3gBRAEAAAAAAAAAAAAAAAAAAKYAAADfDwAAAAAAAAAAAAAAAAAAAAAAAAAApwAAAAAAAABABLgIJAkkCUQHwAAAAAAAAACoAAAAAAAEAAAABAAAAAAAAAAAAAAAAAAAAKkA4AAQAQgC5AQUBRQFFAQEAhgB4AAAAAAAqgASACoAKgA8AAAAAAAAAAAAAAAAAAAAAACrAIAAQAEgAoAAQAEgAgAAAAAAAAAAAAAAAKwAQABAAEAAQABAAMADAAAAAAAAAAAAAAAArQAAAIAAgACAAAAAAAAAAAAAAAAAAAAAAACuABwAIgBZAEkAIgAcAAAAAAAAAAAAAAAAAK8AAAAAAAQABAAEAAQAAAAAAAAAAAAAAAAAsAAIABQAFAAIAAAAAAAAAAAAAAAAAAAAAACxAAAAIAQgBCAEIAT8BSAEIAQgBCAEAAAAALIAAgAxACkAJgAAAAAAAAAAAAAAAAAAAAAAswAQACMAKQAWAAAAAAAAAAAAAAAAAAAAAAC0AAAAAAAAAAQAAgABAAAAAAAAAAAAAAAAALUAAAD4DwACAAIAAfgBAAIAAAAAAAAAAAAAtgAAAAAAAAAgAHgA/AD8D/wHAAAAAAAAAAC3AAAAAAAAAAAAQADgAEAAAAAAAAAAAAAAALgAAAAAAAAAAAoABAAAAAAAAAAAAAAAAAAAuQAAAAIAPgAAAAAAAAAAAAAAAAAAAAAAAAC6ABwAIgAiACIAHAAAAAAAAAAAAAAAAAAAALsAIAJAAYAAIALAAQAAAAAAAAAAAAAAAAAAvAAAAAQA/AAABoABYAAYA4QCwAcAAgAAAAC9AAAABAB8BAADwAAwAAgAJAYgBcAEAAAAAL4AQACEAJQAaAYAAcAAMAIIA4QCwAcAAgAAvwAAAAAHgAhsCAAEAAAAAAAAAAAAAAAAAADAAAAIgAdxAQoBcAGABwAIAAAAAAAAAAAAAMEAAAiAB3ABCgFxAYAHAAgAAAAAAAAAAAAAwgAABMADuQCFALkAwAMABAAAAAAAAAAAAADDAAAGwgG5AIUAugDBAQAGAAAAAAAAAAAAAMQAAAQAB/EAjACMAPEAAAcABAAAAAAAAAAAxQAADIADcgENAXIBgAMADAAAAAAAAAAAAADGAAAEAAPAALAAjAD8B0QERAREBAQEAAAAAMcAAAB8AIIAAQEBCwEFggCAAAAAAAAAAAAAyAAAAPgPiQiKCIgICAgAAAAAAAAAAAAAAADJAAAA+A+ICIoIiQgICAAAAAAAAAAAAAAAAMoAAAD8B0UERQRGBAYEAAAAAAAAAAAAAAAAywAAAPwHRQREBEQEBQQAAAAAAAAAAAAAAADMAAEA+g8AAAAAAAAAAAAAAAAAAAAAAAAAAM0AAAD6DwEAAAAAAAAAAAAAAAAAAAAAAAAAzgABAP0HAQAAAAAAAAAAAAAAAAAAAAAAAADPAAEA/AcAAAEAAAAAAAAAAAAAAAAAAAAAANAAAABAAPwHRAREBAQECALwAQAAAAAAAAAA0QAAAPwHCQAxAMIBAQL9BwAAAAAAAAAAAADSAAAA4AMQBAsICAgICBAE4AMAAAAAAAAAANMAAADgAxAECAgICAsIEATgAwAAAAAAAAAA1AAAAPABCAIFBAUEBQQIAvABAAAAAAAAAADVAAAA8AEJAgUEBgQGBAkC8AEAAAAAAAAAANYAAADwAQgCBQQEBAUECALwAQAAAAAAAAAA1wAAAAAABAIIAZAAYABgAJAACAEEAgAAAADYAAAA8AUIA4QERAQkBBgC9AEAAAAAAAAAANkAAAD4AwEEAggECAAE+AMAAAAAAAAAAAAA2gAAAPgDAAQECAIIAQT4AwAAAAAAAAAAAADbAAAA/AEBAgEEAQQBAvwBAAAAAAAAAAAAANwAAAD8AQECAAQABAEC/AEAAAAAAAAAAAAA3QAIABgAYACGD2EAGAAIAAAAAAAAAAAAAADeAAAA/AcQARABEAEQAeAAAAAAAAAAAAAAAN8AAAD8BwQAAgByBowEAAMAAAAAAAAAAAAA4AAAACADkQSSBJQC4AcAAAAAAAAAAAAAAADhAAAAIAOQBJQEkgLhBwAAAAAAAAAAAAAAAOIAAAAgA5QEkgSSAuQHAAAAAAAAAAAAAAAA4wAAACQDkgSSBJQC4gcAAAAAAAAAAAAAAADkAAAAIAOUBJAElALgBwAAAAAAAAAAAAAAAOUAAAAgA5QEmgSUAuAHAAAAAAAAAAAAAAAA5gAAACADkASQBJAE4AOgBJAEkATgBAAAAADnAAAAcACIAAQLBAWIAAAAAAAAAAAAAAAAAOgAAADAA6MClASQBOAEAAAAAAAAAAAAAAAA6QAAAMADoAKUBJME4AQAAAAAAAAAAAAAAADqAAAAwAOkApIElATgBAAAAAAAAAAAAAAAAOsAAADAA6QCkASUBOAEAAAAAAAAAAAAAAAA7AADAPQHAAAAAAAAAAAAAAAAAAAAAAAAAADtAAAA9AcDAAAAAAAAAAAAAAAAAAAAAAAAAO4ABADyBwQAAAAAAAAAAAAAAAAAAAAAAAAA7wAEAPAHBAAAAAAAAAAAAAAAAAAAAAAAAADwAAAAwAMqBCQEPATiAwAAAAAAAAAAAAAAAPEAAADwByYAEgAUAOIHAAAAAAAAAAAAAAAA8gAAAMABIQISBBQEIALAAQAAAAAAAAAAAADzAAAAwAEgAhQEEgQhAsABAAAAAAAAAAAAAPQAAADAASQCEgQSBCQCwAEAAAAAAAAAAAAA9QAAAMQBIgISBBQEIgLAAQAAAAAAAAAAAAD2AAAAwAEkAhAEFAQgAsABAAAAAAAAAAAAAPcAAAAAAEAAQABAAEwGSARAAEAAQAAAAAAA+AAAAMAFIAIQBdAEIALQAQAAAAAAAAAAAAD5AAAA8AMDBAQEAALwBwAAAAAAAAAAAAAAAPoAAADwAwAEBAQDAvAHAAAAAAAAAAAAAAAA+wAAAPQDBAQCBAQC9AcAAAAAAAAAAAAAAAD8AAAA8AMEBAAEBALwBwAAAAAAAAAAAAAAAP0ACABwAIIJAQfwAAgAAAAAAAAAAAAAAAAA/gAAAP8PCAEEAQQBiABwAAAAAAAAAAAAAAD/AAwIcQiAB4ABeQAEAAAAAAAAAAAAAAAAAAABAATAA7kAhQC5AMADAAQAAAAAAAAAAAAAAQEAACADlASUBJQC5AcAAAAAAAAAAAAAAAACAQAIgAdxAQoBcQGABwAIAAAAAAAAAAAAAAMBAAAiA5QElASUAuIHAAAAAAAAAAAAAAAAEAEAAEAA/AdEBEQEBAQIAvABAAAAAAAAAAARAQAAwAMgBCgEKAT+BwgAAAAAAAAAAAAAABIBAAD8B0UERQRFBAQEAAAAAAAAAAAAAAAAEwEAAMADpAKUBJQE4AQAAAAAAAAAAAAAAAAaAQAA+A+JCIoIiQgICAAAAAAAAAAAAAAAABsBAADAA6IClASSBOAEAAAAAAAAAAAAAAAAKAECAAEA/QcBAAEAAAAAAAAAAAAAAAAAAAApAQQAAgD0BwQAAgAAAAAAAAAAAAAAAAAAACoBAQD9BwEAAAAAAAAAAAAAAAAAAAAAAAAAKwEEAPQHBAAAAAAAAAAAAAAAAAAAAAAAAABDAQAA+A8QAGQAggMBBPgPAAAAAAAAAAAAAEQBAADwByAAFAATAOEHAAAAAAAAAAAAAAAARwEAAPwHCQAyAMIBAQL8BwAAAAAAAAAAAABIAQAA8AciABQAEgDhBwAAAAAAAAAAAAAAAEwBAADwAQgCBQQFBAUECALwAQAAAAAAAAAATQEAAMABJAIUBBQEJALAAQAAAAAAAAAAAABOAQAA4AMQBAkICggJCBAE4AMAAAAAAAAAAE8BAADCASQCFAQUBCICwAEAAAAAAAAAAAAAUgEAAPABCAIEBAQEBAT8B0QERAREBAQEAABTAQAAwAMgBBAEIALAAaACkASQBKAE4AQAAGgBAAD8AQECAQQCBAEC/QEAAAAAAAAAAAAAaQEAAPQDAgQEBAQC8gcAAAAAAAAAAAAAAABqAQAA/AEBAgEEAQQBAvwBAAAAAAAAAAAAAGsBAADwAwQEBAQEAvAHAAAAAAAAAAAAAAAAbAEAAPwBAQICBAIEAQL8AQAAAAAAAAAAAABtAQAA8gMEBAQEBALyBwAAAAAAAAAAAAAAAJIBAAgACCAH+AAkAAQAAAAAAAAAAAAAAAAAoAEAAPABCAIEBAQEBAQMAvMBAAAAAAAAAAChAQAAwAEgAhAEEAQwAswBAAAAAAAAAAAAAK8BAAD8AQACAAQABAAC/AEEAAMAAAAAAAAAsAEAAPADAAQABAAC8AcMAAAAAAAAAAAAAADNAQAIgAdxAQoBcQGABwAIAAAAAAAAAAAAAM4BAAAgA5IElASSAuEHAAAAAAAAAAAAAAAAzwEBAP0HAQAAAAAAAAAAAAAAAAAAAAAAAADQAQIA9AcCAAAAAAAAAAAAAAAAAAAAAAAAANEBAADgAxAECQgKCAkIEATgAwAAAAAAAAAA0gEAAMABIgIUBBQEIgLAAQAAAAAAAAAAAADTAQAA/AEBAgIEAgQBAvwBAAAAAAAAAAAAANQBAADxAwIEBAQCAvEHAAAAAAAAAAAAAAAA1QEAAPgDAgQBCAEIAgT4AwAAAAAAAAAAAADWAQAA8AMFBAEEBQLwBwAAAAAAAAAAAAAAANcBAAD4AwIEAQgBCAME+AMAAAAAAAAAAAAA2AEAAPADBAQBBAQC8AcAAAAAAAAAAAAAAADZAQAA+AMCBAAIAQgCBPgDAAAAAAAAAAAAANoBAADwAwUEAQQFAvAHAAAAAAAAAAAAAAAA2wEAAPgDAwQBCAEIAgT4AwAAAAAAAAAAAADcAQAA8AMEBAEEBALwBwAAAAAAAAAAAAAAAPgBAAD4DxEAYwCEAwAE+A8AAAAAAAAAAAAA+QEAAPAHIQAWABQA4AcAAAAAAAAAAAAAAABRAgAAwAMgBBAEEATwBwAEAAAAAAAAAAAAAGECAADwBAgJBAkECfwHAAAAAAAAAAAAAAAAuwIAABgAFAAAAAAAAAAAAAAAAAAAAAAAAADHAgAAAgAEABgAOAAEAAIAAAAAAAAAAAAAAMkCAAAAAAAABAAEAAQABAAEAAAAAAAAAAAAygIAAAAAEAAIAAQAAgAAAAAAAAAAAAAAAADLAgAAAgAEAAQACAAQAAAAAAAAAAAAAAAAANkCAAAAAGAAYAAAAAAAAAAAAAAAAAAAAAAA6gIAAAAAfgBAAEAAQAAAAAAAAAAAAAAAAADrAgAAAAB+AAgACAAIAAAAAAAAAAAAAAAAAAADAAAEAAgACAAQACAAAAAAAAAAAAAAAAAAAQMAAAAAIAAQAAgABAAAAAAAAAAAAAAAAAAEAwQABAAEAAQAAAAAAAAAAAAAAAAAAAAAAAcDAAAAAAAABgAGAAAAAAAAAAAAAAAAAAAADAMAAAQACAAwAHAACAAEAAAAAAAAAAAAAACRAwAEwAO4AIQAuADAAwAEAAAAAAAAAAAAAJIDAAD8B0QERAREBKQEuAMAAAAAAAAAAAAAkwMAAPwHBAAEAAQABAAAAAAAAAAAAAAAAACUAwAGwAU4BAQEOATABQAGAAAAAAAAAAAAAJUDAAD8B0QERAREBAQEAAAAAAAAAAAAAAAAlgMAAAQEBAfEBCQEHAQEBAAAAAAAAAAAAACXAwAA/AdAAEAAQABAAPwHAAAAAAAAAAAAAJgDAADwAQgCRAREBEQECALwAQAAAAAAAAAAmQMAAPwHAAAAAAAAAAAAAAAAAAAAAAAAAACaAwAA/AdAACAA0AAMAwQEAAAAAAAAAAAAAJsDAATAAzgABAA4AMADAAQAAAAAAAAAAAAAnAMAAPwHCABwAIABAAPwAAgA/AcAAAAAAACdAwAA/AcIADAAwAEAAvwHAAAAAAAAAAAAAJ4DAAAEBEQERAREBEQEBAQAAAAAAAAAAAAAnwMAAPABCAIEBAQEBAQIAvABAAAAAAAAAACgAwAA/AcEAAQABAAEAPwHAAAAAAAAAAAAAKEDAAD8B4QAhACEAEQAeAAAAAAAAAAAAAAAowMAAAQEDAe0BEQEBAQEBAAAAAAAAAAAAACkAwQABAAEAPwHBAAEAAQAAAAAAAAAAAAAAKUDBAAMADAAwAcwAAwABAAAAAAAAAAAAAAApgMAAOAAEAEIAvwHCAIIAhAB4AAAAAAAAACnAwQEGAPgAKAAGAMEBAAAAAAAAAAAAAAAAKgDAAA8AEAAgAD8B4AAQAA8AAAAAAAAAAAAqQMAAPAFCAYEBAQABAQIBvAFAAAAAAAAAACxAwAAwAMgBBAEIALgAxAEAAAAAAAAAAAAALIDAAD+DwEBEQIRAi4CwAEAAAAAAAAAAAAAswMIABgA4AAAD8AAOAAAAAAAAAAAAAAAAAC0AwAAzAMyAjIEIgTEAwAAAAAAAAAAAAAAALUDAABgA5AEkASwBAACAAAAAAAAAAAAAAAAtgMAAOEAGQEFAgMKAQwAAAAAAAAAAAAAAAC3AwAA+AMQAAgACADwDwAAAAAAAAAAAAAAALgDAAD4AUQCQgREAvgBAAAAAAAAAAAAAAAAuQMAAPADAAQAAAAAAAAAAAAAAAAAAAAAAAC6AwAA8AeAAMAAIAMQBAAAAAAAAAAAAAAAALsDAgSCA2QAOADAAwAEAAAAAAAAAAAAAAAAvAMAAPgPAAIAAgAB+AEAAgAAAAAAAAAAAAC9AxAAYACAAwAGwAEwAAAAAAAAAAAAAAAAAL4DAQDPADEBEQIRCgEMAAAAAAAAAAAAAAAAvwMAAOADEAQQBCAEwAMAAAAAAAAAAAAAAADAAwAAEADwBxAAEADwAxAEAAAAAAAAAAAAAMEDAADgDxABCAIIAhAB4AAAAAAAAAAAAAAAwgMAAPAACAEIChAMAAAAAAAAAAAAAAAAAADDAwAA4AMQBBAEMATQAxAAAAAAAAAAAAAAAMQDAAAQABAA8AMQBBAAAAAAAAAAAAAAAAAAxQMAAPADAAQABAAE8AMAAAAAAAAAAAAAAADGAwAA4AAQAQgC/g8IAhAB4AAAAAAAAAAAAMcDCAgwBsABYAEYBggIAAAAAAAAAAAAAAAAyAMAAPgBAAEAAv4PAAIAAfgAAAAAAAAAAADJAwAA4AMQBAAEgAMABAAEMATAAwAAAAAAAAEEAAD8B0UERAREBAUEAAAAAAAAAAAAAAAAEAQABMADuACEALgAwAMABAAAAAAAAAAAAAARBAAA/AdEBEQERAREBIADAAAAAAAAAAAAABIEAAD8B0QERAREBKQEuAMAAAAAAAAAAAAAEwQAAPwHBAAEAAQABAAAAAAAAAAAAAAAAAAUBAAOwAM8AgICAgICAv4DAA4AAAAAAAAAABUEAAD8B0QERAREBAQEAAAAAAAAAAAAAAAAFgQEBAQGmAFgAEAA/AdAAGAAmAEEBgQEAAAXBAAACAJEBEQERAS4BIADAAAAAAAAAAAAABgEAAD8BwACgAFAADAACAD8BwAAAAAAAAAAGQQAAPwHAQKCAUIAMQAIAPwHAAAAAAAAAAAaBAAA/AdAAEAAsAAIAwQEAAAAAAAAAAAAABsEAAQAAvABDAAEAAQA/AcAAAAAAAAAAAAAHAQAAPwHCABwAIABAAPwAAgA/AcAAAAAAAAdBAAA/AdAAEAAQABAAPwHAAAAAAAAAAAAAB4EAADwAQgCBAQEBAQECALwAQAAAAAAAAAAHwQAAPwHBAAEAAQABAD8BwAAAAAAAAAAAAAgBAAA/AeEAIQAhABEAHgAAAAAAAAAAAAAACEEAADwAQgCBAQEBAQECAIAAAAAAAAAAAAAIgQEAAQABAD8BwQABAAEAAAAAAAAAAAAAAAjBAQAGATgBAAD4AAYAAQAAAAAAAAAAAAAACQEAADgABABCAL8BwgCCAIQAeAAAAAAAAAAJQQEBBgD4ACgABgDBAQAAAAAAAAAAAAAAAAmBAAA/gMAAgACAAIAAv4DAA4AAAAAAAAAACcEAAA8AEAAQABAAEAA/AcAAAAAAAAAAAAAKAQAAPwHAAQABAAE/AcABAAEAAT8BwAAAAApBAAA/gMAAgACAAL+AwACAAIAAv4DAA4AACoEBAAEAAQA/AdABEAEQARABIADAAAAAAAAKwQAAPwHQARABEAEQASAAwAA/AcAAAAAAAAsBAAA/AdABEAEQARABIADAAAAAAAAAAAAAC0EAAAIAkQERAREBEgC8AEAAAAAAAAAAAAALgQAAPwHQABAAPABCAIEBAQEBAQIAvABAAAvBAAAOAREA8QARABEAPwHAAAAAAAAAAAAADAEAAAgA5AEkASQAuAHAAAAAAAAAAAAAAAAMQQAAPABSAIkBCQEJATEAwAAAAAAAAAAAAAyBAAA8AeQBJAEkARgAwAAAAAAAAAAAAAAADMEAADwBxAAEAAQAAAAAAAAAAAAAAAAAAAANAQADuADGAIIAggC+AMADgAAAAAAAAAAAAA1BAAAwAOgApAEkATgBAAAAAAAAAAAAAAAADYEEAQwBkABgADwB4AAQAEwBhAEAAAAAAAANwQgABACkASQBOAEAAMAAAAAAAAAAAAAAAA4BAAA8AcAAsABIADwBwAAAAAAAAAAAAAAADkEAADwBwICxAEkAPIHAAAAAAAAAAAAAAAAOgQAAPAHgADAACADEAQAAAAAAAAAAAAAAAA7BAAEAAbwARAAEADwBwAAAAAAAAAAAAAAADwEAADwB2AAgAGAA2AA8AcAAAAAAAAAAAAAPQQAAPAHgACAAIAA8AcAAAAAAAAAAAAAAAA+BAAAwAEgAhAEEAQgAsABAAAAAAAAAAAAAD8EAADwBxAAEAAQAPAHAAAAAAAAAAAAAAAAQAQAAPwPCAEEAQQBhAB4AAAAAAAAAAAAAABBBAAAwAEgAhAEEAQgAgAAAAAAAAAAAAAAAEIEAAAQABAA8AcQABAAAAAAAAAAAAAAAAAAQwQECDgIwASAA3gABAAAAAAAAAAAAAAAAABEBAAA+AAEAQQB/w8EAQQBhAB4AAAAAAAAAEUEEAQwAsABQAEwBhAEAAAAAAAAAAAAAAAARgQAAPgDAAIAAgAC+AMADgAAAAAAAAAAAABHBAAA8AAAAQABAAHwBwAAAAAAAAAAAAAAAEgEAADwBwAEAAQABPAHAAQABPAHAAAAAAAASQQAAPgDAAIAAgAC+AMAAgAC+AMADgAAAABKBAAAEAAQAPAHgASABAADAAAAAAAAAAAAAEsEAADwB4AEgASABAADAADwBwAAAAAAAAAATAQAAPAHgASABIAEAAMAAAAAAAAAAAAAAABNBCACkASQBKACwAEAAAAAAAAAAAAAAAAAAE4EAADwB4AAgADAAyAEEAQgBMADAAAAAAAATwQAAOAEEAMQARAB8AcAAAAAAAAAAAAAAABRBAAAwAOkApAElATgBAAAAAAAAAAAAAAAAAARAAAAAAQABAAEAAQABAAEADwAAAAAAAAAAREAAAAABAAEADwAAAAEAAQAfAAAAAAAAAACEQAAAAA8ACAAIAAgACAAIAAgAAAAAAAAAAMRAAAAADwAJAAkACQAJAAkACQAAAAAAAAABBEAAAAAPAAkACQAAAA8ACQAJAAAAAAAAAAFEQAAAAA0ADQANAA0ADQANAA8AAAAAAAAAAYRAAAAADwAJAAkACQAJAAkADwAAAAAAAAABxEAAAAAPAAoACgAKAAoACgAPAAAAAAAAAAIEQAAAAA8ACgAPAAAADwAKAA8AAAAAAAAAAkRAAAgACAAEAAIAAYACAAQACAAIAAAAAAAChEAAEAAIAAcACAAQAAgABwAIABAAAAAAAALEQAAAAAYACQAJAAkACQAJAAYAAAAAAAAAAwRAABAACQAJAAUAAwAFAAkACQAQAAAAAAADREAAEQAJAAcACQAQAAkABwAJABEAAAAAAAOEQAAQABIAEgAKAAcACgASABIAEAAAAAAAA8RAAAQABQAFAAUABQAFAAUADwAAAAAAAAAEBEAAAAAPAA0ADQANAA0ADQANAAAAAAAAAAREQAAJAAkADwAJAAkACQAPAAkACQAAAAAABIRAAAEADQATABMAE4ATABMADQABAAAAAAAExEAAAAAPAAgACAAIAAEAAQAPAAAAAAAAAAUEQAAAAA8ACAAIAAAADwAIAAgAAAAAAAAABURAAAAADwAIAAgAAAAPAAkACQAAAAAAAAAFhEAAAAAPAAgACAAAAA8ACgAPAAAAAAAAAAXEQAAAAA8ACQAJAAAAAQABAA8AAAAAAAAABgRAAAAADQANAA8AAAAPAAgACAAAAAAAAAAGREAAAAANAA0ADwAAAA0ADQAPAAAAAAAAAAaEQAAAAA0ADQAPAAAABQALgAUAAAAAAAAABsRAAAAABwAXAB8AHwAfABcABwAAAAAAAAAHBEAAAAAPAAkADwAAAA8ACgAPAAAAAAAAAAdEQAAAAAcAFQAdAB0AHQAVAAcAAAAAAAAAB4RAAAAADwAKAA8AAAABAAEADwAAAAAAAAAHxEAAAAAPAAoADwAAAA8ACAAIAAAAAAAAAAgEQAAAAA8ACgAPAAAADwAJAAkAAAAAAAAACERAAAAADwAKAA8AAAAMAAMADAAIAAAAAAAIhEAAAAAPAA8ACAAHAAgAAQAPAAAAAAAAAAjEQAAAAA8ADwAIAAcACAAPAAkACQAAAAAACQRAAAAADwAPAAgABwAIAA8ADwAAAAAAAAAJREAAAAAPAA8ACAAHAAgABwAIAAAAAAAAAAmEQAAAAA8ADwAIAAcACAAHAAkAAAAAAAAACcRAAAAADwAKAA8AAAANAAMADQAAAAAAAAAKBEAAAAAPAAoADwAAAA0AA4ANAAgAAAAAAApEQAAAAA8ACgAPAAAADwANAA0AAAAAAAAACoRAAAAADwAKAA8AAAAPAAkADwAIAAAAAAAKxEAAAAAHABYAHgAeAB4AFgAHAAAAAAAAAAsEQAAAAAeAFQATABgAEwAVAAeAAAAAAAAAC0RAAAgACAAHAAgACAABAAEADwAAAAAAAAALhEAACAAEAAMADAAAAA8ACAAIAAAAAAAAAAvEQAAIAAwAAwAMAAAADwAJAAkAAAAAAAAADARAAAgACAAHAAgAAAANAA0ADwAAAAAAAAAMREAACAAMAAMADAAAAA8ACQAPAAAAAAAAAAyEQAAIAAwAAwAMAAAADwAKAA8AAAAAAAAADMRAAAgABwAIAA8ACgAPAAAADwAAAAAAAAANBEAAAAAIAAcACAAHAAwABwAIAAAAAAAAAA1EQAAIAAQAAwAMAAAABgAJAAYAAAAAAAAADYRAAAgACAAHAAgACAAJAAcACQAIAAAAAAANxEAACAAEAAOABAAIAAUAA4AFAAgAAAAAAA4EQAAIAAgABwAIAAgAAQAFAA8AAAAAAAAADkRAAAgACAAHAAgAAAAPAA0ADQAAAAAAAAAOhEAAEAAMAAcAGAAAAA8ACQAPAAAAAAAAAA7EQAAIAAQAAwAMAAAABQALgAUAAAAAAAAADwRAAAgACAAEAAIAAYACAAQABAAAAAAAAAAPREAAEAAIAAcAFAAQAAgABwAMAAgAAAAAAA+EQAAAAAQABAACAAGAAgAEAAgACAAAAAAAD8RAAAAACAAHABgAAAAMAAcACAAQAAAAAAAQBEAAAAAIAAwACgAJAAmACgAMAAgAAAAAABBEQAAAAAYACQAGAAAAAQABAA8AAAAAAAAAEIRAAAAABgAJAAYAAAAPAAkACQAAAAAAAAAQxEAAAAAGAAkABgAAAA8ACQAPAAAAAAAAABEEQAAAAAYACQAGAAAADwAKAA8AAAAAAAAAEURAAAAABgAJAAYAAAAMAAMABAAIAAAAAAARhEAAAAAGAAkABgAAAA4ACwAMAAAAAAAAABHEQAAAAAYACQAGAAAABgAJAAYAAAAAAAAAEgRAAAAABgAJAAYAAAANAAMADQAAAAAAAAASREAAAAAGAAkABgAAAA0AA4ANAAgAAAAAABKEQAAAAAYACQAGAAAADwANAA0AAAAAAAAAEsRAAAAABgAJAAYAAAAPAAkADwAAAAAAAAATBEAAAAAEAAoACgALAAoACgAEAAAAAAAAABNEQAAIAA0AAwANAAAABgAJAAYAAAAAAAAAE4RAABAACQAJAAUAAwAFAAkACQAAAAAAAAATxEAAEQAJAAcACQAAABkABwAJAAkAAAAAABQEQAAAAAkACQAFAAMABQAJAAkAEAAAAAAAFERAAAkACQAHAAkAEAAFAAcACQARAAAAAAAUhEAACAANAAOADQAAAAUABQAPAAAAAAAAABTEQAAIAAoABwAKAAAABQALgAUAAAAAAAAAFQRAABAAEgASAAoABwAKAAoAEgAAAAAAAAAVREAAAAASAAoACgAHAAoAEgASABAAAAAAABWEQAAAAA8ACQAPAAAADwAKAA8AAAAAAAAAFcRAAAUABQAXAB0AHQAdABcABQAFAAAAAAAWBEAAAAAFAAuABQAAAAUAC4AFAAAAAAAAABZEQAAAAA0AEwATABMAEwATAA0AAAAAAAAAFoRAAAAAAQABAA8AAAAPAAkACQAAAAAAAAAWxEAAAAAPAAgAAAAIAAwAAwAMAAgAAAAAABcEQAAAAA8ACAAIAAAACQAHAAkAAAAAAAAAF0RAAAAADwAIAAgAAAANABOADQAAAAAAAAAXhEAAAAAPAAkACQAAAA0ADQAPAAAAAAAAABfEQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGARAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAYREAAAAAAAAAAAAAAAAAAAAA/gAQAAAAAABiEQAAAAAAAAAAAAAAAAAA/AAQAP4AAAAAAGMRAAAAAAAAAAAAAAAAAAAAAH4AJAAAAAAAZBEAAAAAAAAAAAAAAAAAAP4AJAD+AAAAAABlEQAAAAAAAAAAAAAAAAAAEAD+AAAAAAAAAGYRAAAAAAAAAAAAAAAACAB+AAAAfgAAAAAAZxEAAAAAAAAAAAAAAAAAACgAfAAAAAAAAABoEQAAAAAAAAAAAAAAACgA/AAAAP4AAAAAAGkRAABAAEAAQABAAGAAQABAAEAAQAAAAAAAahFAAEAAQABAAEAAQABAAAAAfgAQAAAAAABrEUAAQABAAEAAQABAAAAAfAAQAH4AAAAAAGwRAABAAEAAQABgAEAAQAAAAH4AAAAAAAAAbRFAAEAAQABgAEAAQABAAGAAQABAAEAAAABuEQAAQABAAEAAQADAAEAAQABAAEAAAAAAAG8RQABAAEAAwABAAEAAQABgAH8AAAAAAAAAcBFAAEAAQADAAEAAQABgAH4AAAB/AAAAAABxEQAAQABAAEAAwABAAEAAAAB+AAAAAAAAAHIRQABAAEAAwABAAEAAQADAAEAAQABAAAAAcxEAAEAAQABAAEAAQABAAEAAQABAAEAAAAB0EUAAQABAAEAAQABAAEAAAAB+AAAAAAAAAHURAAAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAdhEAAEAAQABAAEAAYABAAEAAXgBIAAAAAAB3EQAAQABAAEAAQADAAEAAQABeAEgAAAAAAHgRAABAAEAAQABAAGAAQABAAF4AVAAAAAAAeRFAAEAAYABAAEAAYABAAEAAXgBUAAAAAAB6EQAAQABAAEAAQABgAEAAQABIAF4AQAAAAHsRAABAAEAAQABAAMAAQABAAEgAXgBAAAAAfBEAAEAAQABAAEAAQABAAEAASABeAEAAAAB9EQAAQABAAEAAQABgAEAAQABUAF4AQAAAAH4RAABAAEAAQABAAMAAQABAAFQAXgBAAAAAfxEAAEAAQABAAGAAQABAAAgAfgAAAAAAAACAEYAAgACAAMAAgACAAAgA/gAAAP4AAAAAAIERgACAAIAAwACAAIAAFAD+AAAA/gAAAAAAghEAAMAAwADAAMAA4ADAAMAAwADAAAAAAACDEQAAwADAAMAAwADgAcAAwADAAMAAAAAAAIQRQABAAEAAQABAAEAAQAAAAH4AKAAAAAAAhREAAEAAQABAAEAAQAAAAH4AJAB+AAAAAACGEUAAQABgAEAAQABgAEAAFAB+AAAAAAAAAIcRwADAAMAA4ADAAMAAwADgAMAAwADAAAAAiBFAAEAAYABAAEAAYABAAAAAfgAAAAAAAACJEUAAQABAAMAAQABAAEAAAAD+ABAAAAAAAIoRQABAAEAAwABAAEAAAAD8ABAA/gAAAAAAixHAAMAAwADAAMAAwADAAMAA/gCAAAAAAACMEUAAQABAAMAAQABAABQA/gAAAP4AAAAAAI0RAACgAKAAoACgAKABoACgAKAAoAAAAAAAjhFAAEAAwABAAEAAwABAAAAA/gAQAAAAAACPEUAAQADAAEAAQADAAEAACAD+AAAAAAAAAJARQABAAMAAQABAAMAAAAD8AAAA/gAAAAAAkRFAAEAAwABAAEAAwABAABQA/gAAAAAAAACSEUAAQADAAEAAwABAABQA/gAAAP4AAAAAAJMRoACgAKAAoACgAKABoACgAKAAoACgAAAAlBFAAEAAwABAAEAAwABAAAAA/gAAAAAAAACVEQAAoACgAKAAoACgAaAAoACgAKAAAAAAAJYRAACgAKAAoACgAKAAoACgAKAAoACgAAAAlxFAAMAAwADAAMAAwADAAIAAvgCAAAAAAACYEQAAAAAAAAAAAAAAAAAA+AB+ABAAAAAAAJkRAAAAAAAAAAAAAAAAAAA8AP4AJAAAAAAAmhEAAEAAQABAAEAAYABAAEAAQABeAEAAAACbEQAAQABAAEAAQADAAEAAQABAAF4AQAAAAJwRAABAAEAAQABAAEAAQABAAEAAXgBAAAAAnREAAAAAAAAAAAAAAAAAAAAAXgAAAAAAAACeEQAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAJ8RAAAAAAAAAABAAAAAAAAIAH4AAAAAAAAAoBEAAIAAgACAAIAAoAGAAIAAgACAAAAAAAChEQAAAAAAAAAAQAAAAAAAAAB+AAAAAAAAAKIRAAAAAAAAQAAAAAAAAABAAAAAAAAAAAAAoxEAAEAAQABAAEAAQABAAEAAXgBIAAAAAACkEQAAQABAAEAAQADAAEAAQABeAFQAAAAAAKURAAAAAAAAAAAAAAAAJAA+APwAJAAAAAAAphFAAEAAQABAAEAAQABAAAAAfgAoAAAAAACnEUAAQABAAEAAQABAAAAAfgAkAH4AAAAAAKgRAAAAAAABAAEAAQABAAEAAQAPAAAAAAAAqREAAAAAAAEAAQAPAAAAAQABAA8AAAAAAACqEQAAAAAAAQABAA8AAAAMAAMABAAIAAAAAKsRAAAAAAAOAAgACAAIAAgACAAIAAgAAAAArBEAAAAAAA8ACAAIAAgADQADAA0AAAAAAACtEQAAAAAADgAIAAgAAAAEAA8ABgAAAAAAAK4RAAAAAAAPAAkACQAJAAkACQAJAAAAAAAArxEAAAAAAA0ADQANAA0ADQANAA8AAAAAAACwEQAAAAAADQANAA8AAAABAAEADwAAAAAAALERAAAAAAANAA0ADwAAAA8ACQAPAAAAAAAAshEAAAAAAA0ADQAPAAAADwAKAA8AAAAAAACzEQAAAAAADQANAA8AAAAIAAcACAAIAAAAALQRAAAAAAANAA0ADwAAAA8ADQANAAAAAAAAtREAAAAAAA0ADQAPAAAADwAJAA8ACQAAAAC2EQAAAAAADQANAA8AAAAKAAsACgAAAAAAALcRAAAAAAAPAAkACQAJAAkACQAPAAAAAAAAuBEAAAAAAA8ACgAKAAoACgAKAA8AAAAAAAC5EQAAAAAADwAKAA8AAAAMAAMADAAIAAAAALoRAAAAAAAIAAgABAADAAQACAAIAAAAAAAAuxEAAAAIAAQAAwAEAAgABAADAAQACAAAAAC8EQAAAAAABgAJAAkACQAJAAkABgAAAAAAAL0RAAAAAAAJAAkABQADAAMABQAJAAkAAAAAvhEAAAAAAAoACgAGAAcABgAKAAoAAAAAAAC/EQAAAAAABQAFAAUABQAFAAUADwAAAAAAAMARAAAAAAAPAA0ADQANAA0ADQANAAAAAAAAwREAAAAAAAkADwAJAAkACQAPAAkAAAAAAADCEQAAAAAAAgAKAA4ADwAOAAoAAgAAAAAAAMMRAAAAAAABAAEADwAAAA0ADQAPAAAAAAAAxBEAAAABAAEADwAEAAMADAABAA8AAAAAAADFEQAAAAAADwAIAAgACAABAAEADwAAAAAAAMYRAAAAAAAPAAgACAAAAA8ACQAJAAAAAAAAxxEAAAAAAA8ACAAAAAgADAADAAwACAAAAADIEQAAAAAADwAIAAgAAAAMAAsADAAAAAAAAMkRAAAAAAAPAAgACAAAAA8ADQANAAAAAAAAyhEAAAAAAA8ACQAJAAAAAQABAA8AAAAAAADLEQAAAAAADwAJAAkAAAANAA0ADwAAAAAAAMwRAAAADwALAAsAAAABAA8ADAADAAwAAAAAzREAAAAAAA0ADQAPAAAADwAIAAgAAAAAAADOEQAAAAAADQANAA8AAAAPAAkACQAAAAAAAM8RAAAAAAAPAAEADgAJAAEABAALAAUAAAAA0BEAAAAAAA0ADQAPAAAADQANAA8AAAAAAADREQAAAA8ACwABAA4ACQAPAAAAAQAPAAAAANIRAAAADwALAAEADgAJAA8ABAADAAwAAAAA0xEAAAANAA0ADgAHAAoADwAMAAMADAAAAADUEQAAAA0ADQAOAA8ACgAPAAQACwAFAAAAANURAAAAAIAGgAaABwAAgAsAC4ALAAAAAAAA1hEAAAAPAAsACQAMAAMADAAEAAMADAAAAADXEQAAAAAADQANAA8AAAAMAAsADAAAAAAAANgRAAAAAAANAA0ADwAAAAUABQAPAAAAAAAA2REAAAAAAA0ADQAPAAAABQALAAUAAAAAAADaEQAAAAAADwAJAA8AAAABAAEADwAAAAAAANsRAAAAAAAPAAkADwAAAA0ADQAPAAAAAAAA3BEAAAAAAA8ACQAPAAAADwAKAA8AAAAAAADdEQAAAAAADwAJAA8AAAAMAAMADAAIAAAAAN4RAAAADwAJAA8ADAADAAwABAADAAwAAAAA3xEAAAAAAA8ACQAPAAAADgALAAwAAAAAAADgEQAAAAAADwAJAA8AAAAKAAcACgAIAAAAAOERAAAAAAAPAAkADwAAAAoACwAKAAAAAAAA4hEAAAAAAAcABQANAA0ADQAFAAcAAAAAAADjEQAAAAAADwAKAA8AAAANAA0ADwAAAAAAAOQRAAAAAAAPAAoADwAAAA8ACQAPAAkAAAAA5REAAAAAAA8ACgAOAAAACgALAAoAAAAAAADmEQAAAAAABwAGAA4ADgAOAAYABwAAAAAAAOcRAAAACAAEAAMABAAIAAEAAQAPAAAAAAAA6BEAAAAIAAwAAwAMAAAADwAJAAkAAAAAAADpEQAAAAgACAAHAAgAAAANAA0ADwAAAAAAAOoRAAAACAAMAAMADAAAAA8ACgAPAAAAAAAA6xEAAAAAAAgACAAMAAsACgAMAAgACAAAAADsEQAAAAAABgAJAAYAAAABAAEADwAAAAAAAO0RAAAABgAJAAYAAAABAA8AAAABAA8AAAAA7hEAAAAAAAYACQAGAAAABgAJAAYAAAAAAADvEQAAAAAABgAJAAYAAAAFAAUADwAAAAAAAPARAAAAAAAEAAoACgALAAoACgAEAAAAAAAA8REAAAAAAAYACQAGAAAADAADAAwABAAAAADyEQAAAAAABgAJAAYAAAAOAAsADAAAAAAAAPMRAAAACQAPAAkADwAAAA8ACgAPAAAAAAAA9BEAAIACgAKAA4AKgAqACoADgAIAAgAAAAD1EQAAAAAACgALAAoAAAAOAAgACAAAAAAAAPYRAAAAAAAKAAsACgAAAA0ADQAPAAAAAAAA9xEAAAAAAAoACwAKAAAADwAJAA8AAAAAAAD4EQAAAAAACgALAAoAAAAOAAoADwAAAAAAAPkRAAAAAAAFAAsACwALAAsACwAFAAAAAAAA+hEAAAAAAAEAAQAPAAAADwAIAAgAAAAAAAD7EQAAAAAAAQABAA8AAAAPAAoADwAAAAAAAPwRAAAAAAABAAEADwAAAAoABwAKAAgAAAAA/REAAAAAAAEAAQAPAAAABQAFAA8AAAAAAAD+EQAAAAAAAQABAA8AAAAKAAsACgAAAAAAAP8RAAAAAAAPAAgACAAAAA8ACAAIAAAAAAAAPh4AAPgPEADgAAQDAgbhARAA+A8AAAAAAAA/HgAA8AcgABAAEADkByMAEQAQAOAHAAAAAKAeAAHAADwAIwg8AMAAAAEAAAAAAAAAAAAAoR4AAMgAJAEkCaQA+AEAAAAAAAAAAAAAAACiHgAIAAbgARkBMgHAAQAOAAAAAAAAAAAAAKMeAAAgA5AEkgSUAuAHAAAAAAAAAAAAAAAApB4ACIAHcgEKAXABgwcACAAAAAAAAAAAAAClHgAAIAOUBJQElALgBwYAAAAAAAAAAAAAAKYeAAiDB3ABCgFyAYAHAAgAAAAAAAAAAAAApx4AACMDkASUBJQC5AcAAAAAAAAAAAAAAACoHgAAAAyIA2QCNALBAwYMAAAAAAAAAAAAAKkeAAAgA6gElASVAu4HAAAAAAAAAAAAAAAAqh4AAAAMCQPlAhYC5gIJAwAMAAAAAAAAAACrHgAAIAORBJUElQLhBwAAAAAAAAAAAAAAAKweAAKAAXkARwh5AIABAAIAAAAAAAAAAAAArR4AAJABSgJJCkoB8gMAAAAAAAAAAAAAAACuHgAAAAiEB2gEywQIBwAIAAAAAAAAAAAAAK8eAAAkA5gEmgSZAuQHAAAAAAAAAAAAAAAAsB4AAAAMwwM1AmQCggMADAAAAAAAAAAAAACxHgAAJAOZBJoEmALkBwAAAAAAAAAAAAAAALIeAAAADMIDNAIlAsQCAgMADAAAAAAAAAAAsx4AAEQGKAkzCTAFzA8AAAAAAAAAAAAAAAC0HgAIAA6AAXIBDAF1AYQBAA4ACAAAAAAAALUeAAAkA5kEmQSZAuUHAAAAAAAAAAAAAAAAth4ABAAD8ACNCPAAAAMABAAAAAAAAAAAAAC3HgAAkQFKAkoKSgHxAwAAAAAAAAAAAAAAALgeAAD/AREBEQkRAQABAAAAAAAAAAAAAAAAuR4AAPAAKAEkCSQBOAEAAAAAAAAAAAAAAAC6HgAA+A+ICIkIigiICAAAAAAAAAAAAAAAALseAADAAaAClgSUBOAEAAAAAAAAAAAAAAAAvB4AAP0HRQRGBEYEBQQAAAAAAAAAAAAAAAC9HgAAxAOiApIElATiBAAAAAAAAAAAAAAAAL4eAAD4D4oIigiKCAoIAQAAAAAAAAAAAAAAvx4AAMADpAKUBJAE5gQAAAAAAAAAAAAAAADAHgAA+w+ICIoIiggICAAAAAAAAAAAAAAAAMEeAADDA6gCpASUBOAEAAAAAAAAAAAAAAAAwh4AAPAPFAEUARQBEQEWAAAAAAAAAAAAAADDHgAAwAOkApQEkQTmBAAAAAAAAAAAAAAAAMQeAAD4D4kIiwiLCIkIAAgAAAAAAAAAAAAAxR4AAMIDqQKlBJEE4QQAAAAAAAAAAAAAAADGHgAA/gMjAiMKIwIAAgAAAAAAAAAAAAAAAMceAADgAVICSQpKAnACAAAAAAAAAAAAAAAAyB4AAPkPAgAAAAAAAAAAAAAAAAAAAAAAAADJHgAA9gcEAAAAAAAAAAAAAAAAAAAAAAAAAMoeAAD/CQAAAAAAAAAAAAAAAAAAAAAAAAAAyx4AAP0JAAAAAAAAAAAAAAAAAAAAAAAAAADMHgAAfACCAAEBAQkBAYIAfAAAAAAAAAAAAM0eAABwAIgABAkEAYgAcAAAAAAAAAAAAAAAzh4AAOADEAQICAkICggQBOADAAAAAAAAAADPHgAAwAEgAhIEFAQgAsABAAAAAAAAAAAAANAeAADgAxAECggKCAgIEwTgAwAAAAAAAAAA0R4AAMABJAIUBCQEKALCAQAAAAAAAAAAAADSHgAA4AMTBAgICggKCBAE4AMAAAAAAAAAANMeAADDASgCJAQUBCQCwAEAAAAAAAAAAAAA1B4AAOADEAQKCAoICAgTBOADAAAAAAAAAADVHgAAwAEkAhQEJAQhAsYBAAAAAAAAAAAAANYeAADgAxQEEggLCBMIFATgAwAAAAAAAAAA1x4AAMABKQIlBBUEIQLAAQAAAAAAAAAAAADYHgAA+AAEAQMCAwoDAgQB+AAAAAAAAAAAANkeAADgABIBCQoKAhIB4AAAAAAAAAAAAAAA2h4AAOADEAQICAgICwgYBOYDAAAAAAAAAADbHgAAwAEgAhQEEgQxAswBAAAAAAAAAAAAANweAADgAxAECwgICAgIGATmAwAAAAAAAAAA3R4AAMABIQISBBQEMALMAQAAAAAAAAAAAADeHgAA4AMQBAgICQgKCBgE5gMAAAAAAAAAAN8eAADAASACFgQUBDACzAEAAAAAAAAAAAAA4B4AAPABCQIFBAYEBgQMAvIBAAAAAAAAAADhHgAAxAEiAhIEFAQwAswBAAAAAAAAAAAAAOIeAADwAQgCBAQEDAQEDALzAQAAAAAAAAAA4x4AAHAAiAAECQQBjABzAAAAAAAAAAAAAADkHgAAfwCAAAABAAkAAf8AAAAAAAAAAAAAAOUeAAD8AAABAAmAAPwBAAAAAAAAAAAAAAAA5h4AAPgHAAgFCAIIAAT4AwAAAAAAAAAAAADnHgAA8AMABAYEBALwBwAAAAAAAAAAAAAAAOgeAAD4AwAEBAgCCAEE+AMIAAYAAAAAAAAA6R4AAPADAAQEBAIC8AcMAAAAAAAAAAAAAADqHgAA+AMBBAIIBAgABPgDCAAGAAAAAAAAAOseAADwAwIEBAQAAvAHDAAAAAAAAAAAAAAA7B4AAPgHAAgFCAIIAAT4AwgABgAAAAAAAADtHgAA8AMABAYEBALwBwwAAAAAAAAAAAAAAO4eAAD8AQECAQQCBAEC/QEEAAMAAAAAAAAA7x4AAPQDAgQEBAQC8AcMAAAAAAAAAAAAAADwHgAA/AEAAgAEAAwABPwDBAADAAAAAAAAAPEeAAD8AAABAAmAAPwBAwAAAAAAAAAAAAAA8h4IABgAYQCGD2AAGAAIAAAAAAAAAAAAAADzHggAcACBCQIH8AAIAAAAAAAAAAAAAAAAAPQeAQADAAwA8AkMAAMAAQAAAAAAAAAAAAAA9R4AAAwIcAiAB+AAHAQAAAAAAAAAAAAAAAD2HggAEABlAIYPYAAYAAgAAAAAAAAAAAAAAPceGADgAAMPggFwAAgAAAAAAAAAAAAAAAAA+B4EAA0AMQDCBzEADQAEAAAAAAAAAAAAAAD5HgQAHQjgBAED4QAcAAAAAAAAAAAAAAAAAAIgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQIAAAAAAAAAAAQABAAEAAAAAAAAAAAAAAABEgAACAAIAAgAAAAAAAAAAAAAAAAAAAAAAAEiAAAIAAgACAAIAAgAAAAAAAAAAAAAAAAAATIAAAgACAAIAAgACAAAAAAAAAAAAAAAAAABQggACAAIAAgACAAIAAgACAAIAAgAAAAAAAFSAAAEAAQABAAEAAQABAAEAAQABAAEAAAAAWIAAAAAAAAAAAAAD/D/4HAAAAAAAAAAAAABggAAAAAAAAAAAAAAAAAAAAAAAAAAAGAA0AGSAWAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAaIAAAAAoABgAAAAAAAAAAAAAAAAAAAAAAABwgAAAAAAAAAAAAAAAAAAAGAA0AAAAGAA0AHSAWAAwAAAAWAAwAAAAAAAAAAAAAAAAAAAAeIAAAAAoABgAAAA4AAAAAAAAAAAAAAAAAACAgAAAAAAAAIAAgAPwPIAAgAAAAAAAAAAAAISAAAAAAAAAQARAB/g8QARABAAAAAAAAAAAiIAAAAAAAAAAAQADgAEAAAAAAAAAAAAAAACUgAAAAAGAAQAAAAAAAAAAAAGAAQAAAAAAAJiAAAGAAQAAAAAAAYABAAAAAAABgAEAAAAAnIAAAAAAAAAAAQADgAEAAAAAAAAAAAAAAADAgeACECIQG+AFgAJgDRASAA4ADQARABIADMiAAABwAAAAAAAAAAAAAAAAAAAAAAAAAAAAzIAAAHAAEABAADAAAAAAAAAAAAAAAAAAAADUgAAAAAAAAAAABAAEAAgAAAAAAAAAAAAAAOSCAAEABIAIAAAAAAAAAAAAAAAAAAAAAAAA6ICACQAGAAAAAAAAAAAAAAAAAAAAAAAAAADsgAAAAAGQECAOQAGQGaASQAQgCRAQAAAAAPCAAAPwGAAAAAAAA/AYAAAAAAAAAAAAAAABCIAAAgAIAAcAHlAK+AggAlALABwABgAIAAEcgAAAIAIQGZAAYAAAACACEBmQAGAAAAAAASCAAAAgAhAZkABgAAAAAAPwGAAAAAAAAAABJIAAA/AYAAAAACAAEAMQGOAAAAAAAAAAAAFEgAAAAAAAAlAIUA94HCAOUAgAAAAAAAAAAdCAAABwAEgA/ABAAAAAAAAAAAAAAAAAAAACpIPgDQAf4APAAQAf4AwAAAAAAAAAAAAAAAKsgAADgBRgGGAb8AwgAAAAAAAAAAAAAAAAArCDAANABqAKkBKQECAIAAAAAAAAAAAAAAADdIOAAGAMEBAQEAggCCAIIAggEBAQEGAPgAN4g/g8CCAIIAggCCAIIAggCCAIIAggCCP4PACEAADAASAAkBjwBgABgABADjAREBEAAAAADIQgAFAAUAAgAAADwAQgCBAQEBAQECAIAAAUhAAAwAEgARAYEAcAAIACYA0QEQASAAwAACSEAAAgAFAAUAAgAAAD8B0QARABEAAQAAAAKIQAAAAAAAHAGiAmECEQIRAe4AAQAAAAAAA8hAAAAAAAAAAT4A0wAKAAoBMADAAAAAAAAEyEAAfwDQgQiBBwCAAAAAAAAAAAAAAAAAAAWIQAA+AcQAGAAgAEAAvwHAAAwAUgBSAEwASEhAAAEAPwHBAAAAPAHkASQBAAA8AcABAAEIiEEAHwABAAAAHwAGAAAAHwAAAAAAAAAAAAmIQAAAADwBAgFBAYEAAQABAYIBfAEAAAAACchAAAAAOQBFAIMBAAEAAQMBBQC5AEAAAAAKyEAAAAAAAgABuYBGQEZAeYBAAYACAAAAAAuIUAA8AFIAkQERAREBEQESAJwAkAAAAAAADUhAAAAAMwFUAYgAEAAgADkABgBCAYAAAAAOyEAAPwHRABEAAAH+ACMAPABBAa8A+AAHAeQIUAA4ABQAVABQABAAEAAQABAAEAAQAAAAJEhAAAAAAAAGAAEAP4PBAAYAAAAAAAAAAAAkiEAAEAAQABAAEAAQABAAEAAUAFQAeAAQACTIQAAAAAAAAADAAT+DwAEAAMAAAAAAAAAAJQhQADgAFABUAFAAEAAQABQAVAB4ABAAAAAlSEAAAAAAAAYAwQE/g8EBBgDAAAAAAAAAACWIQAAAAB4ABgAKABIAIAAAAEAAgAEAAAAAJchAAAAAAAEAAIAAYAASAAoABgAeAAAAAAAmCEAAAAABAAIABAAIABAAoADAAPAAwAAAACZIQAAAADAAwADgAJAAiAAEAAIAAQAAAAAALghAgACAHoAGgAqAEoAggACAQICAgQCAAAAuSHIDwgCCAeICkgKCAIIAkoCKgIcAggCPgLEIQgCCAeICkgKCAIIAggCSgIqAhwCCAIAAMUhAAAYAAQA/g8EABgAAAMABP4PAAQAAwAAxiEIAhwCKgJKAggCCAIIAkgKiAoIBwgCAADLIRABEAEYARQBEAEQARABEAUQAxABEAEAAMwhEAEQARADEAUQARABEAEUARgBEAEQAQAA0CFAAKAAEAEcBxQFEAEQARABEAEQARABAADSIQAAEAEQARABEAEQARABFAUcBxABoABAANQhQACgABABHAcQARABEAEcBxABoABAAAAA5iFAAKAAEAEIAhwHEAEQARABEAEQARAB8AHnIQAAAAAgADAA7A8CCAII7A8wACAAAAAAAOgh8AEQARABEAEQARABEAEcBwgCEAGgAEAA6SEAAAAAgACAAf4GAggCCP4GgAGAAAAAAAD1IQAAAAMABP4PAAQAAxgABAD+DwQAGAAAAAAiAAAEABgA4AAgAyAEIAPgABgABAAAAAAAAiIAAAAAAACAA0gEJAQkBMQDeAAAAAAAAAADIgAAAAAAAEQERAREBEQERAREBPwHAAAAAAUiAADgABADCAOEBEQERAQkBBgCGAHgAAAABiIAAAAAAAaABXAEDAQMBHAEgAUABgAAAAAHIgAAAAAMADQAxAEEBgQGxAE0AAwAAAAAAAgiAADgAFABSAJIAkgCSAJIAkgCSAIAAAAACSIAAOAAUAFQAUgCSAL8B0gCSAJIAkgCAAAKIgAAAAAAAMADoAKQBJAEkAQQAAAAAAAAAAsiAAAAAEgCSAJIAkgCSAJIAkgCUAHgAAAADyIAAAAAAAD8DwQABAAEAAQA/A8AAAAAAAARIgAAAAAAAAwGFAWkBEQEBAQEBAAAAAAAABIiQABAAEAAQABAAEAAAAAAAAAAAAAAAAAAEyIAAIQAhACEAIQA9AeEAIQAhACEAAAAAAAVIgAIAAQAAgABgABAACAAEAAIAAQAAgAAABoiAAKAAQAOAAPgABgABgABAAEAAQABAAEAHSLgABABEAEQAaAAQACgABABEAEQAQAAAAAeIuAAEAEQARABoABAAEAAoAAQARABEAHgAB8iAAAAAPwHAAQABAAEAAQABAAEAAQABAAAICIAAAAEAAYABYAEQAQgBBAECAQABAAAAAAjIgAAAAAAAAAAAAD8BwAAAAAAAAAAAAAAACUiAAAAAAADwAAgBBgDhABgABgAAAAAAAAAJiIAAAAAAAPQACAEWAPEACABGAAAAAAAAAAnIgAAAAQAA8AAMAAMADAAwAAAAwAEAAAAACgiAAAEABgAYACAAQAGgAFgABgABAAAAAAAKSIAAAAA8AcIAAQABAAEAAQACADwBwAAAAAqIgAAAAD8AQACAAQABAAEAAQAAvwBAAAAACsiAAAAAAAAAAgACPwHAgACAAAAAAAAAAAALCIAAAAIAAj8BwIAAgAACAAI/AcCAAIAAAAtIgAIAAj8BwIAAAj8BwIAAggACPwHAgACAC4iAAAAAAAAAADgCPwHogDiAAAAAAAAAAAANCIAAAAAAAMAAgAAGAAQAAAAAAMAAgAAAAA1IgAAAAAYABAAAAAAAwACAAAYABAAAAAAADYiAAAAAAAAAAAAABgDEAIAAAAAAAAAAAAANyIAAAAAGAMQAgAAAAAAAAAAGAMQAgAAAAA9IuAAEAEQARABgACAAGAAIAAQARABEAHgAEMiAAAgARABEAEQARABIAEgASABEAEAAAAARSIAAKACkAKQApACoAKgAqACoAKQAgAAAABIIgAAIAGQAJAAkAAQASABIAEgAZAAAAAAAEwiOABEBUQFRAUgBSAFGAUIBUQFRAVEBTgAUiIAAAAAoACsAKgAoACgAKAGoAKgAAAAAABgIgAAoACgAKAAoAfgALwAoACgAKAAAAAAAGEiAABIAkgCSAJIAkgCSAJIAkgCSAIAAAAAYiIAAFABUAFQAVAH8AFcAVABUAFQAQAAAABkIgAAIAQgBDAEUARIBIgEhASEBAQFAAAAAGUiAAAAAAQFhASEBIgESARQBDAEIAQgBAAAZiIAABAKEAowCigKKApECkQKhAqCCgAAAABnIgAAAACCCoQKRApECigKKAowChAKEAoAAGoiQABAAKAAEAFIAqgCpAQQAQgCCAIEBAAAayIAAAQECAIIAhABpASoAkgCEAGgAEAAQABuIgAAQADAAMAAIA/gARwCCAIIBAQIAAAAAG8iAAAAAAQICAQQAhAO8AEsAcAAwABAAAAAciIAAAAAEAgQBDACKAJIBEQIRAiCBIICAABzIgAAAACCCIIERAJEAkgEKAgwCBAEEAIAAHYiAABQCFAIWASYBKgEpAIkA0QBQgFCAQAAdyIAAEIBQgFEASQDpAKoBJgEWARQCFAIAACCIgAA4AAQAQgCCAIIAggCCAIIAggCAAAAAIMiAAAAAAgCCAIIAggCCAIIAggCEAHgAAAAhCIAAOAAEAEIAQgGiANIAjgCDAIIAgAAAACFIgAAAAAIAggGiANIAjgCDAIQAhAB4AAAAIYiAAAwBEgEhASEBIQEhASEBIQEhAQAAAAAhyIAAAAAhASEBIQEhASEBIQEhARIBDAEAACKIgAAMARIBIQEhASEDIQGhASEBIQEAAAAAIsiAAAAAIQEhASEBIQMhAaEBIQESAQwBAAAlSIAAOAAGANIAkQE9AVEBEQESAIQAeAAAACWIgAA4AAQAUgCRAREBEQERARIAhAB4AAAAJciAADgABABCAKkBEQERASkBAgCEAHgAAAAmCIAAOAAEAEIAoQERAREBCQECAIQAeAAAACZIgAA4AAQAQgCBAREBAQEBAQIAhAB4AAAAKAiAAD8BwQEFAWkBEQERASkBBQFBAT8BwAApSIAAAAEAAQABAAE/AcABAAEAAQABAAAAAC/IgAAAAQABgAFgARABCAEEAQIBPwHAAAAANoiAABICUgJSAlMBVQFVAZSBlICYgJiAgAA2yIAAAAAUgJSAlICVAZUBUwFSAlICUgJAADvIgAAYABAAAAAAABgAEAAAAAAAGAAQAAAAAUjAAAAAAgGCAGIAEgAaACIAAgBCAYAAAAABiMAAAAAFAYUAdQANAA0ANQAFAEUBgAAAAAHIwAAAAAAAAAAYQiSBJIEDAMAAAAAAAAAABIjCAAEAAQAAgACAAIAAgACAAQABAAIAAAAGCMMBhIJEgkSCfwHEAEQAfwHEgkSCRIJDAYpIwAAAAAAAAAAAAAAAAAAQACwAQwGAggAACojAAACCAwGsAFAAAAAAAAAAAAAAAAAAAAAsCMAAAAAAAAAAAAAAAj+BwEAAAAAAAAAAACxIwAAAAAAAAAAAAABAP4HAAgAAAAAAAAAAL4jAAAAAAAAAAAAAP8PAAAAAAAAAAAAAAAAvyMAAAAAAAAAAAAA/w8AAAAAAAAAAAAAAADAIwAAAADwAAgBBAL/DwQCBAIIAfAAAAAAAMEjAAAAAHAAjAEEAf8PBAEEAYgAcAAAAAAAwiMAAAAA4AAYAwgC/w8IAggCEAHgAAAAAADDIwAAAAPAAjACCAL/DwgCMALAAgADAAAAAMQjAAAAAAADwAIwAggC/w8IAjACwAIAAwAAxSMACAAIAAvACjAKCAr/DwgKMArACgALAAjGIwAAAABgABAAEAD/DyAAQABAACAAAAAAAMcjAAAAAGAAEAAQAP8PIABAAEAAIAAAAAAAyCMAAAAAwAAgACAA/w9AAIAAgABAAAAAAADJIwAAAAAAAAAAAAAAAP8PAAAAAAAAAAAAAMojAAAAAAAAAAAAAAAA/w8AAAAAAAAAAAAAyyMAAAAAAAAAAAAAAAD/DwAAAAAAAAAAAADMIwAAAAAAAAAAAAAAAP8PAAAAAAAAAAAAAM4jgAFAAkAEIARwDkACQAI+AgIBggF+AAAA2iMAAAAAgACAAoAGgAb+BoACgACAAAAAAADbI0AAQADwAVABUAFQAVABUAFQAfABQABAACMkAAAADAAIAAgADAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABMAAAAAAAAAAAAAAgAEAAgAAAAAAAAAAAAAIwAAAAAAAAAABgAJAAkABgAAAAAAAAAAAAAzAAAAAAAAPAADAAAAAAA+AAEAAAAAAAAAAEMPABCAYECAILggTyASAIIAgcBAQECAPwAAUwAAAAAYAAYAAYARQBEAKQA3AEEAAAAAAABjAAAAAAAA8QBBACoAFAAKAAkAAMAQAAAAAHMAAA8AEIAgQEAggCCAIIAggEBAgC8AEAAAgwAAAAAAAAAAAAAAAAAABAALABDAYCCAAACTAAAAIIDAawAUAAAAAAAAAAAAAAAAAAAAAKMAAAAAAAAAAAAAAAAPAADANiDJgBBg4AAAswAAAGDpgBYgwMA/AAAAAAAAAAAAAAAAAADDAAAAAAAAAAAAAAAAAAAAAA/gECAAIAAgANMAAIAAgACPAPAAAAAAAAAAAAAAAAAAAAAA4wAAAAAAAAAAAAAAAAAAD+AQIB/gEGAAYADzAADAAM8A8QCPAPAAAAAAAAAAAAAAAAAAAQMAAAAAAAAAAAAAAAAAAAAAD+DwYMAggAABEwAAACCAYM/g8AAAAAAAAAAAAAAAAAAAAAEjAAAAAAJAAkACQA5AckACQAJAAAAAAAAAATMAAAngeeB54HngeeB54HngeeB54HngcAABQwAAAAAAAAAAAAAAAAAAAAAPwHBAQCCAAAFTAAAAIIBAT8BwAAAAAAAAAAAAAAAAAAAAAWMAAAAAAAAAAAAAAAAAAA/w8BCP0LAwwBCBcwAQgDDP0LAQj/DwAAAAAAAAAAAAAAAAAAGDAAAAAAAAAAAAAAAAAAAAAA/AP+BwUKAgQZMAIEBQr6BfwDAAAAAAAAAAAAAAAAAAAAABowAAAAAAAAAAAAAAAAAAAAAP4PAgj+DwIIGzACCP4PAgj+DwAAAAAAAAAAAAAAAAAAAAAcMEAAQAAgACAAIABAAIAAgACAAEAAQAAAAB0wAAAAAAAAAAAAAAAAAAAAABIAFAAkAAAAHjAAACQAFAASAAAAAAAAAAAAAAAAAAAAAAAfMAAAgAQABQAJAAAAAAAAAAAAAAAAAAAAACAw4ADAAJQB1ALUBPQGFAaUBNQElAOAAGAAITAAAAAAAAAAAAAA/gcAAAAAAAAAAAAAAAAiMAAAAAAAAAAA/AMAAAAA/gcAAAAAAAAAACMwAAAAAAAA/AMAAAAA/AEAAAAA/gcAAAAAJDAAAAAECAQIAhABoADAALAADAEABgAAAAAlMAAAAAAAAJgDVgIiBCAEVAKYAQAAAAAAACYwAAAgACAAIAAgACAALAAgACAAIAAgACAAJzAQABAAEAEQARABFAEQARABEAEQABAAAAAoMBAEEASQBJAEkASUBJAEkASQBBAEEAQAACkwAAAAAAgEKARIAogCDgGIAmgCGAQABAAAKjAADAAIAAAAAAAAAAAAAAAAAAAAAAAAAAArMAcAAgAAAAAAAAAAAAAAAAAAAAAAAAAAACwwBwACAAAAAAAAAAAAAAAAAAAAAAAAAAAALTAADAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAuMGAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAC8wsAEgAQAAAAAAAAAAAAAAAAAAAAAAAAAAMDAgAEAAgACAAEAAIAAgAEAAgACAAEAAIAAxMAAAAAAcAGMAgAEAAgAMAAAAAAAAAAAAADIwAAAAABwAYwCAAQACAAwAAAAAAAAAAAAAMzAAAAAAAAwAA8AAMAAMAAIAAAAAAAAAAAA0MAAAAAAADAADwAAwAAwAwgAAAGAAAAAAADUwAAAAAAIADAAQAGAAgAEABgAIAAAAAAAANjDgARgCBARUBFII0gtSCFIIVAQEBBgC4AE3MAIMDAPwABgBBg4AAAYMGAPgABwDAgwAADgwIAAgACAAIAAgAPwPIAAgACAAIAAgAAAAOTAAABAAEAAQAP4HEAAQAP4HEAAQABAAAAA6MAAAIAggBvwBIAAgAPwHIAAgAP4PIAAgADswAAAAAAAAQADGCCgFMAUwAhACAAAAAAAAPDAAAPwHBAYEBYQERAQkBCQEFAQMBPwHAAA9MAAAGAAEAAQACAAEAAgAMADAAAABAAYAAD4wqgoCCAAA0gjQANIJ0gjQAAoIAAACCKoKPzAAAAAAAAD+DwII8gmyCQII/g8AAAAAAABBMAAAAAAQAhAFkAT4B1AB0AiQBIADAAAAAEIwAAAEA4gESAT8AywGqAFoCCgERAKAAQAAQzAAAAAA8AEAAgAEAAMAABAAYACAAwAAAABEMAAAAAD4AQACAAQAAgABAAAIABgA4AEAAEUwAAAAAEAAgABQCFAIUAhQBIADAAAAAAAARjAAAAAAEAAgABIEFAQUBBQCJAHgAAAAAABHMAAAAAAACEAESAJQAdAGUAgACAAIAAAAAEgwAAAAAAAEIAIiAaQBZAEkAhQEAAQABAAASTAAAAAAIAYgCfgHoACgCIAIkAQgAyAAAABKMAAAEAMQBZAE/ANQAFAESARAApgDIAAAAEswAAAQBBAD8AAeBBAEEALgAQgAGABgAAAATDAAABAEEAPwAB4EEAQQAuABCAAaAOAABgBNMAAAAAAoB6gIKAgoCDwJaAmoCSQAAAAAAE4wAAAAACgHKAgoCCgIPgloCaAJJgAAAAYATzAAAAAAAADAAKAAEAEIAgwEBAgAAAAAAABQMAAAAADAAKAAEAEIAgwEZAgAADAAAAAAAFEwAAD+AwIFAAAQABAEEAwQAv4BEAAQAAAAUjAAAP4BAgcAAAAAEAQQDBAC/AESABAABgBTMAAAAACAAwgECAQIBAgECAQIBAAEAAAAAFQwAAAAAIADCAQIBAgECAQIBAAEBAQAAAYAVTAAAAAAEAeQBBAIEAicCLAI0AiIAAAAAABWMAAAAAAQB5AEEAgQCJwIsAjQCJYAAAAGAFcwAAAAAAAA/AcACAAIAAgACAAEAAQAAgAAWDAAAAAAAAD8BwAIAAgACBAIBAQcBAACAABZMAAAAAAIAAgAyAgoBT4DyAEIAAgACAAAAFowAAAIAAgAyAgoBSgD/gEIAAoACAAKAAAAWzAgACAAIAD8AyAEIASQBJAEfAQQBBAAAABcMCAAIAAgAPwDIAQgBBAFEAX8BBIEEAAGAF0wAABAAEAAJACkA3QELAgkCCQIIAAAAAAAXjAAAEAAQAAkAKQDdAQsCCQIKAggAAgAAABfMAAAAAAQBpABfAAUAAADIAQgBCAEAAQAAGAwAAAQBpABeAAUABADCAQgBCAELAQAAAwAYTAAAAgACAH4AI4ISAhICEgIiASEAwAAAABiMAAACAAIAfgAjghICEgISAiABIQDAQACAGMwAAAAAEAAQABABCAEIAQgAkACwAEAAAAAZDAAACAAEAAQABAECAQIBAgCCAIQAeAAAABlMAAAIAAQABAAEAQIBAgECAIQAhYB4AAGAGYwAAAEAAgACAAEAOQBFAIMAgQEBAQEAAAAZzAEAAgACAAEAOQBFAIMAgQENAQEADAAAABoMAAAAACCA04EMAQgBBAECAQIBAAEAAAAAGkwAAAAAAQHnAhgCEAIIAgQCBAIBAgAAAwAajAAAAgBiAFoAB4GCAkECeAHCAIIBBAAAABrMAAA8AcMC4AAAAAAAwgECAQIBAgECAQAAGwwAACAA3wE4AQQA8gAPgIIBQgFEALgAwAEbTAIAhABiAD+BygAIAAQAggFCAUQBeADAAJuMAAA4AEQAggCBAH0AAwEBAQIAhgB4AAAAG8wAAD8BwIBAAAQAhAFEAUQBfwDEAIQAgAAcDAAAPwPAgIAABAGEAkQCRAJ+AcSAhAEBgBxMAAA+A8EAgAAEAYQCRAJEAn4BxQCCgQEAHIwAAAIAMgHKAQYCAQIAAgABvwBQACAAAAAczAAAAgAyAMoBBgIBAgACAAG/AFAAIoAAgB0MAAACADIAygEGAgECAAIAAb4AUQAigAEAHUwAAAAAgABgAACBCIExAQYAwAAQACAAQACdjAAAAACAAGAAAIEIgTEBBgDAABcAIABDAJ3MAAAAAIAAYAABARmBIQEGAMAAMgAFAMIAngwAACAAEAAIAAQAAgAEAAgAEAAgAAAAQACeTAAAYAAQAAgABAAIABAAIgAEAEAAhgEAAB6MAAAAAGAAGAAEAAQACAAQACYACQBGAYABHswAAD4BwYBAAAgAiQFJAUkBfwDJAIkAgACfDAAAPAPDAMAACAGKAkoCfgJKAYoAgQEAAB9MAAA8A8MAwAAIAYoCSgJ+AkoBi4CCQQGAH4wAAAAACgGKAkoCSgJ/AcoAigCKAQAAAAAfzAAAAADgAREAsQBdABMCEAEgAPgAAABAACAMAAAyAEoAj4GyAkICAwIAAgECAgHEAAAAIEwAACAA0QEOATQAogDeAgOCAgEEALgAQAAgjAAAJAAkACQB/wIkgiQCAAIQASAAwAAAACDMAAAgACQAHAAwANIDCgBIAEgAcAAAAAAAIQwAABAACQAOADQARIOigiIAIgAiABwAAAAhTAAAAAA8AOQAEAAQA0gAvADIALAAQAAAACGMAAAAAD8A0AAIAEQCQgG/gMIAhAB4AAAAIcwAAAAAAAGAAkACfgJIAYgAiAEAAQAAAAAiDAAAAAGAAkACQAJ/AkQBhACEAIQBAAEAACJMAAAAADwAZAEhAhECEQISAiABIADAAAAAIowAAAAAAAA/gASCAgEBAQEBAgD8AAAAAAAizAAAIABRABEBiQJNAksDiQIRASAAwAAAACMMBACEAGQAP4HIAAQAAgACACIA3AEAAQAAo0wAAAAAYAARAAkCDQILAgkBEQEgAMAAAAAjjAAAAAAIAIgAfgPQAAgBCAEIALAAQAAAACPMBACEAGQAPwPIAAgABAEEAQQBCACwAEAAJAwAACAA0AEJAOkAHQGHAkQCRAKIATAAwAAkTAACCAEFAJUA7QCvAzUBJQCVAJgAgAMAACSMAAAiABIADgGLgmoCMgLSAhICCAIIAAAAJMwAAAABoABYABYAEQAgAMABAAEAAIAAQAAlDAAABAAIAASBBQEFAQUAiAB5AAAAAwAAACVMAAAAABACEAMwAM4CCAIwAcQAGAAgAEAAJYwAAAAAPAHEAoAAEAAQAhADPADQABAAAAAmTAGAAAABgAAAAAAAAAAAAAAAAAAAAAAAACaMAIABQACAAAAAAAAAAAAAAAAAAAAAAAAAJswDAAAAAYAAAAAAAAAAAAAAAAAAAAAAAAAnDAEAAoABAAAAAAAAAAAAAAAAAAAAAAAAACdMAAAAAAAAAAACAAQAiABQAGAAAAAAAAAAJ4wAAAAAAAAAAAIABACIAHMAQABDAAAAAAAnzAAAAAIAAYAAcAEcAhOCEoEiAMQAAAAAACgMAAAAAAAAKAAoACgAKAAoAAAAAAAAAAAAKEwAAAAABAEEAwQAtABEADQADAAEAAAAAAAojAAAAQABAQEAgQD9AAEACQAFAAMAAQAAACjMAAAAAAAAQABgACAAMAPIAAwABAAAAAAAKQwAABAAEAAIAAgABAA8AcIAAQAAgAAAAAApTAAAAAA4AAgACAIMAQgBCAC4AEgAAAAAACmMAAAAAB4AAgACAQOBAgCCAHIADgAAAAAAKcwAAAABCAEIAQgBOAHIAQgBCAEAAQAAAAAqDAAAAgCCAIIAggC+AMIAggCCAIIAgACAACpMAAAAAAABCACIAGgCGAI+A8gACAAIAAAAKowAAAAAhABEAGQAFAEMAT8BxAAEAAQAAAAqzAAABAEEAQQAtABPAAQBBAEEAbwAQAAAACsMAAAEAQQBBAC0AE8ABAEEAQWBvABBgAAAK0wAAAAACABIAEQARwB8AOQDJAAiACAAAAArjAAACABIAEQARwB8AGQDpAAhgCIAAIAAACvMAAAQAAgBBAMCAQOAggBiABoABgAAAAAALAwAABAACAEEAwIBA4CCAGIAEgAOgAAAAMAsTAAAIAAYAAQCBwIEAQQA/AAEAAQABAAAACyMAAAgABgABAIHAgQBBAD8AAQABQAEAAGALMwAAAAAAgCCAIIAggCCAIIAggC+AcAAAAAtDAAAAAACAQIBAgECAQIBAgECgT6DwIAAAC1MAAAEAAQAPwAEAQQBBAEEAP8ABAAEAAAALYwAAAQABAA/AAQBBAEEAL8ARAAFAACAAAAtzAAACAAIAhECAgEEAQAAgABAAHAACAAAAC4MAAAEAAgBEIEBAIIAgABgABEADAAJgAAALkwAAAACAgECAQIAggBiABIATgCCAQACAAAujAAAAAICAQIBAgCCAHIADgBBgYICAIAAAC7MAAAIAAgACAA/gMQBBAEkATIBCgEGAAAALwwIAAgACAA/gMQBBAEkATIBCoEGAADAAAAvTAAAAQAGAAgBAACAAIAAcAAMAAMAAAAAAC+MAAACAAwAEAIAAQABAADgAB6AAgAAgAAAL8wAABAACAIEARIBE4CiAGIAWgCGAAAAAAAwDAAAEAAIAgQCEgETgKIAogBSAM6AQQAAwDBMAAAQABEAEQIRAREBvwBRABEAEIAQAAAAMIwQABIAEgISARIAvgBSABEAFQAQAAYAAAAwzAAAAAAIADACAAIMARAAgABwAAwAAAAAADEMAAACAAwAAAEAAQcAhABAAHAADQACAAAAMUwAAAYAGAAAAgACBgEIAIAAYABZgAQAAYAxjAAACAAIAAkBCQEJALkASQAJAAkACAAAADHMAAAIAAgACQEJAQkAuQBJAAkACAAJQABAMgwAAAAAAAAAAD8D0AAQABAAIAAgAAAAAAAyTAAAAAAAAAAAPwPQABAAEAAmACAAAwAAADKMAAAEAAQABAEEAIQAf4AEAAQABAAEAAAAMswAAAAAggCCAIIAggCCAIIAggCCAIAAgAAzDAAAAAAAAhICEgEiAIIAYgBeAIIBAAAAADNMAAAAAEIAQgBiACIAM4HKACYAIgAAAEAAc4wAAAAAAAEAAQAAgABgABAADgABAAAAAAAzzAAAAAEAAPgABgAAAAAAAgAcACAAQAGAADQMAAAAAQAA8AAOAAAAAAACAAwAMYACAcCANEwAAAABAAD4AAYAAAAAAAIADAAxAEKBgQA0jAAAAAAAAD8AyAEIAQgBBAEEAQIBAAGAADTMAAAAAD8AyAEIAQgBBAEEAQWBAAGBgAAANQwAAAAAPwDIAQgBCAEIAQQBBQECgAEAAAA1TAAAAAACAAIBAgICAQIAggCiAFoABgAAADWMAAAAAAIAAgECAwIBAgCCAGIAH4ACAACANcwAAAIAAgACAgIBAgECAIIAcgAPAAKAAQA2DAAAIAAQAAgABAACAAQACAAQACAAAABAALZMAABgABAACAAEAAgAEAAiAAQAQACGAQAANowAACAAUAAIAAQABAAIABAAIgAFAEIBgAA2zAAAAABEAHQABAEEAT8BxAAEADQABABAAHcMAACEAPQABAIEAj8DxAAEADcABgDAgIAAN0wAAIQA9AAEAgQCPwPEAAQANQACgMEAgAA3jAAAAgACABIAIgACAGIAogMaAAYAAgAAADfMAAAAAAAASQCJAIkAkQCSAJIBAgEAAAAAOAwAAAABAAEAAfwBAwEAAIAAsACAAMADAAA4TAAAAAEEAIQAiABoABAALAADAEAAgAAAADiMAAAAABEAEQARAD8A0QERAREBEQEQAQAAOMwAAAAAIAAQABwAMADQAxAASAB4AAgAAAA5DAAACAAIAAkAHgAkAcQCJAAUAAwAAgAAADlMAAAAAAABCAEIAQgBCAE4AcgBAAEAAAAAOYwAAAAAggCCAIIAggCCAIIA/gCAAIAAgAA5zAAAAAAEASQBJAEkASQBJAE8AcAAAAAAADoMAAAAABIBEgESARIBEgESARIBPgPAAAAAOkwAAAAACAAJAAkCCQEJAQkAiQBpABgAAAA6jAAAAAAAAD8AAAAAAgABAAC/AEAAAAAAADrMAAAAAQAA/wAAAAAAPwHAAIAAgABgAAAAOwwAAAAAAAA/AcABAACAAIAAQABgABAAAAA7TAAAAAA+AcIAggCCAIIAggCCAL4BwAAAADuMAAAAADwABAAEAgQBBACEAHwABAAAAAAAO8wAAAAAHgACAAICAgECAQIAggB6AAYAAAA8DAAAAABEAEQAfABEAEQAfwPEAEQAQABAADxMAAACAQIBAgECAToBwgESAQoBBgECAQAAPIwAAAAAEQARAhECEQERAREAkQB5AAcAAAA8zAAAAAABAQEBAgCEAIAAQABgABgABAAAAD0MAAAeAAIAAgECAQOAggCCAHKADgAAwAAAPUwAAAAACAIIAQgA/gAIAggCCAI4AcAAAAA9jAAAAAAgABAADgIKAQgA+AAIAAgACAAAAD3MAAAeAAIAAgICAQIBAgCCAHqABgAAgAAAPgwAAEQARAB8AEQARABEAH8DxABFAECAQAA+TAAAAgECAQIBAgE6AcIBEgEKgQaBAoEAAD6MAAASABICEgISARIBEgCSAH6AAgAAgAAAPswAAAAAAAAAABAAOAAQAAAAAAAAAAAAAAA/DAAAEAAQABAAEAAQABAAEAAQABAAEAAAAD9MAAAAAAAAAAAEAAgAEAAgAEAAgAAAAAAAP4wAAAAAAAAEAAgAEAAmAEAAgwAAAAAAAAA/zAAAAAABAAEAAQABAAEAAQA/A8AAAAAAAAFMQAAAAAYABYAEAAQCBAIEAgQB/AAAAAAAAYxAAAACBAIXgSQBJACEAGQAnAEEAgAAAAABzEAAAAA/AcEAAQABAAEAAQABAD8BwAAAAAIMQAA/AMEBAQEBAQEBAQEBAQEBAQEAAAAAAkxAAAACBAIHgQQA/AAEAgQCBAE8AMAAAAACjEAABAAEAQQB9AEOAQWBJAEEAMQDBAIAAALMQAAAAAEAAQABAAkADQILAgkCCAH4AAAAAwxAAAABBwEEgKQAXAAHgQQBBAE8AMAAAAADTEAACAA0AAMAQICIARQAIgABgMCBAAAAAAOMQAABAAEADQALAgkCCQIJAzkAwQABAAAAA8xAAQABvwBBAAEAAQABAAEAAQABAAEAAAAEDEAAAAAAAH8AYAAgACAAIAA/gcAAAAAAAARMQAAAAAAAEAAoAAQAQgCBgQCCAAAAAAAABIxAAAAAAQABAAEAAQA/AcEAAQABAAEAAAAEzEAAAAE/ASABIAE/geABIAEgAT8BAAEAAAUMQAAAAAgAZAAiABIAMQPIgAQAAgAAAAAABUxAAgABuQBJAAkACQAJAAkACQAPAAAAAAAFjEAAAAA/AcEBBQEJATEBIQEBAT8DwAAAAAXMQAAAAAEAAQABAD8DwQABAEEAQQB/AAAABgxAAAIAAgAiAD4AI4AiAiIDIgDCAAIAAAAGTEAAAAGgANAAjgCBgIAAkACwAEAAwAEAAAaMQAAAAACAAQACAAQAOAHEAAIAAQAAgAAABsxAAAAAIgDiAJIBEgEeAQIBAgCCAIIAgAAHDEAAAAAiANIAkgESAR+BAgECAIIAggCAAAdMQAAIAAgAPwDIAQgBCAEoAV8BCAEIAAgAB4xAABECHQERAJEAeQAXABEAEQMxANEAAAAHzEAABAACAAIAAQAOABAAIAAAAEAAgAEAAAgMQAAAABADGAEWAbEBUIEIASYBAADAAwAACExAAAABAQEFAIkAkQBhABkARwCBAQAAAAAIjEAAAAARAB0AEQARABEAEQAdAhMD8AAAAAjMQAAAABAAHgARABAAEAAQABACEAHwAAAACQxAAAABBAEEAIQAZAA/gMQBBAEEAQQBBAAJTEAAAAAAAQAB8AEMAQMBAAEAAQABAAEAAAmMQAAAAgABAAD/gAAAAAA/gMABAAEAAQABCcxAABAAEAAQABAAEAAQABAAEAAQAAAAAAAKDEAAAAEBAIIAZAAYABgAJgABgMABAAAAAApMQAAAAD8BwAEAAQABAAEAAQABPwPAAAAACoxAAQEBAQChAF8ACQEJAQkBCQG5AEEAAAAKzEAAAQEBAQEA/wABAAEAPwDBAQEBAQEAAQsMQAAAAb4AQgACAAKAA4ACAAIAAgACAAAAC0xAAAEAOQHJAAkACQA/A8kACQA5AcEAAAALjEAAAgAyANIBEgESAR6BAgECAQIBAgECAAvMQAABAAEACQA9AcsACwEJAQgBuABAAAAADExAAAAABAAEAAQABAAEAAQAPADAAAAAAAAMjEAABAAEAAQAPADAAAQABAAEADwAwAAAAAzMQAAAAAQABAA8AMAAAAD8AAAAQACAAAAADQxAAAAAPgBAAEAAQABAAEAAQABAAAAAAAANTEAAAAA8AMAAgACEAIQAfAAEAEQAgAAAAA2MQAAAADwAQABAACgAWACcAKgAQAAAAAAADcxAAAAAPABEAEQARABEAEQARABAAAAAAAAODEAAAAA8AEQARABAADwARABEAEAAAAAAAA5MQAAAADIA0gCSAJIAkgCSAJ4AgAAAAAAADoxAAAAANABUAFwAQABEAAQAPABAAAAAAAAOzEAAAAA0AFQAXABAADwARABEAHwAQAAAAA8MQAAAADQAVABcAEAAPABIAEgAfABAAAAAD0xAAAAANABUAFwAQABgABwAIAAAAEAAAAAPjEAAAAA0AFQAXABAADwAVABUAEQAQAAAAA/MQAAAADQAVABcAAAAfABEAHwARABAAAAAEAxAAAAANABUAFwAQAAkAFYApABAAAAAAAAQTEAAAAA8AEQARABEAEQARAB8AEAAAAAAABCMQAAAAD4ASABIAEgASABIAH4AQAAAAAAAEMxAAAAAPABQAHwAQAA8AFAAfABAAAAAAAARDEAAAAA8AFAAfABAAGAAHAAgAAAAQAAAABFMQAAAAAAAYAAQAA4AEAAgAAAAQAAAAAAAEYxAAAAAoABcACAAQACgAHwAAABAAIAAAAARzEAAAAA4AAQAQgCCAIIAhAB4AAAAAAAAABIMQAAAAAIAYgASAA4AEgAiAAIAQAAAAAAAEkxAAAAAYgAeACIAAABiAB4AIgAAAEAAAAASjEAAAAAEAEQAZAAfACQABABEAEAAAAAAABLMQAAAABIAEgASABIAEgASAD4AQAAAAAAAEwxAAAAAPABUAFQAVABUAFQAVABAAAAAAAATTEAAAABEAHwARABEAEQAfABEAEAAQAAAABOMQAAAAAQAJABUAJYAlACkAEQAAAAAAAAAE8xAAAAAAAAAAAAAPwPQABAAAAAAAAAAAAAUDEAAAAAAAAAAP4HIAD+DwAAAAAAAAAAAABRMQAAAAAAAAAA/g+QAJAAAAAAAAAAAAAAAFIxAAAAAAAAAAD+D5AA/g8AAAAAAAAAAAAAUzEAAAAAAAAgACAAIAD+BwAAAAAAAAAAAABUMQAAAAAAACAAIAD+BwAA/g8AAAAAAAAAAFUxAAAAAAAAkACQAJAA/g8AAAAAAAAAAAAAVjEAAAAAAACQAJAA/g8AAP4PAAAAAAAAAABXMQAAAAEAAQABAAHwAQABAAEAAQABAAAAAFgxAAAAAQABAAHgAQABAAEAAPwPQABAAAAAWTEAAAABAAHgAQABAAEAAPwPQAD8DwAAAABaMQAAAAEAAQAB8AEAAQABAAD+DwAAAAAAAFsxAAEAAQAB+AEAAQABAAH4AQABAAEAAAAAXDEAACAAIAAgACAA4AcgACAAIAAgAAAAAABdMQAAQABAAEAAwAdAAEAAQAEAAfwPAAAAAF4xAABAAEAAwAdAAEABAAH8DwAA/g8AAAAAXzEAAEAAQABAAMAHQABAAEAAAAD+DwAAAABgMSAAIAAgAOAHIAAgACAA4AcgACAAIAAAAGExAABAAEAAQABAAEAAQABAAEAAQABAAAAAYjEAAIAAgACAAIAAgACAAIAAAAD+DwAAAABjMQAAAAAAAAAAAAD+DwAAAAAAAAAAAAAAAGQxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZTEAAAAA+AEAAQABAAD4AQABAAEAAQAAAABmMQAAAADwAQABAAEAAPABEAEQAQABAAAAAGcxAAAAAPgBAAEAAQABgAB4AIAAAAEAAAAAaDEAAAAA+AEAAQAAAAHAATgB4AEAAQAAAABpMQAA0AFQAXABAAEQAPABgABwAIABAAAAAGoxAAAAANABUAFwAQAA8AEQARABAAEAAAAAazEAANABUAEgAPABIAHwAYAAcACAAQAAAABsMQAAAADQAVABcAAAAcABMAHAAQAAAAAAAG0xAAAAANABUAFwAQAAkAFQAlACkAEAAAAAbjEAAAAA8AEQAfABAADwASABIAHwAQAAAABvMQAAAADwARAB8AEAAYAAcACAAAABAAAAAHAxAAAAAPABEAHwAYAAQAFwAYABAAAAAAAAcTEAAAAAAAB8AEQDxAREA3wAAAAAAAAAAAByMQAAAADwAUABQAHwAQAAEADwAQAAAAAAAHMxAAAAAPABQAHwAQAA8AEQARABAAAAAAAAdDEAAPABIAHwAYAAcACAARABEADwAQAAAAB1MQAA8AEgAfABgABwAIAA8AEQAQABAAAAAHYxAAAAAPABQAHwAQAAkAFwAJAAEAEAAAAAdzEAAAAA8AFAAfABAADwAVABUAEAAAAAAAB4MQAAAAAAAHwASAPIBEgDfAAAAAAAAAAAAHkxAAAAAHwASAA8A4AEPANIAHwAAAAAAAAAejEAAAACAAHwAAABEAIQABAA8AMAAAAAAAB7MQAAAAGAAHgAgAAAAfgBAAEAAQAAAAAAAHwxAAAAAYAAcACAAAAB8AEQARABAAAAAAAAfTEAAAABgABwAIAAAAHwAUAB8AEAAAAAAAB+MQAAAAIAAfAAAAEQAhAB8AAQARACAAAAAH8xAAAAAAAAAAKAA3gCIALAAwACAAAAAAAAgDEAAAAA4AAQARAB4ADgABABEAHgAAAAAACBMQAAAADAACABEAIYAhACIAHAAAAAAAAAAIIxAAAAAOAAGAEgAeAAgAF4AIAAAAEAAAAAgzEAAAAA4AAYASAB4ACAAXgBIAHAAQAAAACEMQAAAAAAAEQAfAPEBEQDfABEAAAAAAAAAIUxAAAAAJABWAKQAQAAkAFYApABAAAAAAAAhjEAAAAAAADIASgCKAIoAsgBAAAAAAAAAACHMQABAAHwAQABAAHwAQABAAD+D5AAkAAAAIgxAAAAAfABAAHwAQABAAD+D5AA/g8AAAAAiTEAAAABAAHgAQAB4AEAAQAA/g8AAAAAAACKMQAAIADgByAAIADgByAAoAKAAv4PAAAAAIsxAAAgAOAPIADgDyAAgAL+DwAA/g8AAAAAjDEAAEAAQADAB0AAQADAB0AAAAD8DwAAAACNMQAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAI4xAAAAAAAAAABgAAAA/g8AAAAAAAAAAAAAkDEAAAAAAAAAAAAAAAD8BwAAAAAAAAAAAACRMQAAAAAAAPwHAAIAAgACAAGAAIAAYAAAAJIxAABAAEAAQABAAEAAQABAAEAAQABAAEAAkzEAAAAECAQIBAgECAQIBAgECAQIBAgEAASUMQAAAAREBEQERAREBEQERAREBAQEAAQAAJUxAAD8DwQExAU8BAQEBAT8BIQEBAT8DwAAljEAAAAEAAQABAAE/gcgBCAEIAQgBAAEAACXMQAA+AGIAIgAiAD+D4gAiACIAIgA+AEAAJgxBAAEAAQABAAEAPwPJABEAEQAhAAEAAAAmTEAAPwBpACkAKQA/A+kAKQApAD8AQAAAACaMQAABAYECYQIRAgkCBQIDAgECAAIAAcAAJsxAAD0DxQAFAOUAHwAlACUABQLFAj0DwAAnDEAAAQABAAECAQIBAj8BwQABAAEAAQABACdMQQIRAhEBEQERAP8AEQBRAJEBEQIRAgAAJ4xAAIQAv4DEAFAAPwHIAgQCP4JCAj4CAAGnzEACAAEAAQAA8AAPADgAAABAAIABAAIAACgMQAAAAAYABYAEAAQBhAJEAkQB/AAAAAAAKExAAAEAAQEBAoECvwHBACEAIQAhAB8AAAAojEAAAAAAAH8AIAAgASACkAK/gcAAAAAAACjMQAAAABgAJgABgMiDHAAiAEGAgINAAkABqQxAAAgACAAIAD+AxAEEAQQBFAEcAQYAAAApTEAACAAIAAgAP4DEAQQBBAEUAYwDQgCAACmMQAABACEAEQBJAIcDAQEBAIEAgQBhAAAAKcxAAAEAIQBRAI0BAwIBASEBUQCRAKEBQAAqDEABAQECAIQAaAAQADgAFABTAJCBEAEAACpMQAABAAEAAgEEAogCuAHEAQQAAgABAAAAKoxAABAAEAAQABAAEAAQADAAGAAUAAgAAAAqzEAAAAEBAQIAhABoABAAKACGAMGBQIFAAKsMQAA/AcEAAQABAAEAPwHBAAEAAQA/AcAAK0xAAAEDAQD/AAEAAQA/A8EAPwDBAQEBAAArjEAAkQFdAVEAkQFxAB8AEQARATEAwQAAACvMQAAAABABGAGUAVMBcQEIAwQBgAFAAIAALAxAAAAAOIPJgAoADAA4A8wACwAIgDgDwAAsTEAAPQHFACUAHQBHAIUAhQBlAAUAPQHAACyMQAACAAIBAgECAQIBPgHCAQIBAgECAQIALMxAAAAAAAAAAYACQAJ/gcAAgACAAAAAAAAtDEAAAABwAEACQAJAAcAAAAAAAAAAAAAAAC1MQAAAAnABQADAAkABwAAAAAAAAAAAAAAALYxAABAAMABQAlACUAHQAAAAAAAAAAAAAAAtzEAAAAIgAeAAIAAgACAAAAAAAAAAAAAAAC4MQAAAAAIAggDiAKIAkgCKAIYAggCAAIAALkxAAAAAPgDAAIAAgACAAIAAgACAAIAAgAAujEAACAAIAAgACAAIAD8AyAAIAAgACAAIAC7MQAAAAeACAACAAWACAAAAAAAAAAAAAAAAPAxAAAAAIAAgAhACDAEKAQgAqABYAAAAAAA8TEAAAAAIARIBEgCEAIAAQABgABgAAAAAADyMQAAAAAABBAEEAIQAZAAcAEQAgAEAAAAAPMxAAAAAAAAAADwD4AAgAAAAQABAAAAAAAA9DEAAAAAAAhQBJAEkAIQAfACEAQAAAAAAAD1MQAAAAAABAAD8AAAAAAAMADAAQAGAAAAAPYxAAAAAAAA+ANABEAEIAQgBCAEAAAAAAAA9zEAAAAAAAAQABAEEAQQAhAB0AAwAAAAAAD4MQAAAAAAAYAAQAAgAEAAgAAAAQACAAQAAPkxAAAgAiABoAQgBPgHIACgACADAAIAAAAA+jEAAAAAAAQABIAHcAQIBAAEgAIAAwAMAAD7MQAAAABAAFAEUAhQBFACUAHAAAAAAAAAAPwxAAAAAAAA8AEACAAIAAgABvABAAAAAAAA/TEAAAAAAAgABvABAADwDwAEAAQAAwAAAAD+MQAAAAAAAAAA8A8ACAAEAAQAAgABgAAAAP8xAAAAAPAPEAQQBBAEEAQQBBAE8A8AAAAAAE4AAEAAQABAAEAAQABAAEAAQABAAEAAQAABTgAABAAEAAQIBAgECPwHBAAEAAQABAAAAANOAABAAEAAQAD8ByAIIAggCCAIIAgQBgAAB04ACAQIBASEA3wAJAAkCCQIJAzkAwQAAAAITgAACAgIBGgEiAQIA4gDfgQIBAgECAgABAlOAAAABEQERAREBEQERAREBEQEBAQABAAACk4AAAAEAAQABAAE/gcgBCAEIAQgBAAEAAALTgQABAAEAAQABAD8DyQARABEAIQABAAAAAxOAAgEBAQC/AEEAAQABAAEAPwPBAAEAAAADU4EAYQAhABEACQA/A8EACQARACEAAQBAAAOTgAAAAFAAXwBSAFIAUgJSAlICEgEyAMAABBOAACCAPIAggCCAP4AkgiSCJIIkgaCAQAAEU4AAEQIRAhECPwPRAhECEQIxA/8CAAIAAgTTgAAIAAkAKQC/ASmBKQEpAqkCaQAIAAAABROAAAACAAI/A8kCSQJJAkkCSQJ/A8ACAAIFk4QABAA/gcQBBAE/gUQBRAFEAX+BRAEEAAYTgAEAAT8ByQEJAQkBCIE4gciBCAEAAQAABlOAAD0DxQAFAOUAHwAlACUCBQLFAj0DwAAGk4AABgE4AQABP4HAAQABP4HAATgBBgEAAAbTgAIAArACT4IQAhACgAJwAg+CMAIAAsAChxOAAAEDGQCVAFOCEUI9A9EAEQBRAIEBAAAHU4gBDAF7AUiBRgFAAQwBawFYgUQBQgFAAAfTkAAVARUBlQF1AR8BFQEVAVUA1QEQAgAACFOAAAEAPQPFADUBxQC/AMUAtQLFAj0BwQAIk5AAFQEVAZUBdQEfARUBFIFUgNSDEAIAAAkTgQA9A8UABQD/ACUAhQB/ACUCBQJ9AcAACVOAAzoAyQAJAA8ACQAJAA8ACQALAAsACAAJk4ABCgEyQUOBPgHCAQIBPgHDgTKBSgEAAQnTgAAQABMCNwPRAT+BEQBRAJUBUQJRAgAACpOQAAgABAACAAEAOYPBAAIABAAIABAAAAAK04AAAIABAAEAAgA4A8QAAgABAACAAAAAAAtTgAA+AGIAIgAiAD+D4gAiACIAIgA+AEAADBOAAEoASgBKAEoAf4PKAEoASgBKAEAAQAAMk4AAMADXAJUAlQC/g9UAlQCVAJcAsADAAA0TgAA/AMAAP4PAADYD0YEVATUB1QERATEDzhOAAgoBEgCSAH/AAgDCAEIAPgHAAgACAAGOU5ACEAG/AFEAFQAVABkCGQIRAj8B0AAQAA6TgAAEAgSBBQCkAF+ABAA0AkQCRAM8AMAADtOAAiICIgIiAiKCPoPjAiICIgIiAgACAAAPU4AAAQA9A+UABQI9AcEAPQPlAAUCPQHBAA+TkgAKgJuAlgCSQLqD0gCSAIeAioCSAAAAENOAAgEBIQDfAAEAAQABAR0CEwIQAjABwAARU4AAIAIYAgQBA4CCgGIAPgACAMABAAEAAhITgAAQAAgBBAGCAWGBEIEIASYBAAFAAYACElOAAgECBwEYAKCAg4BgAJgAhgEBAgACAAAS04AAAAICAQIAggFCQmOCEwIKAgYCAgIAAhMTgACAAL8AoQChAKGAoQCpAqkCpwIgAcAAE1OAABgABAADAAKAPgPSAFIAUgBSAFIAQgATk4AAIAAjAC8CIQIhAj8B4QAwgDyAIoAgABPTgAAAAgkBCQCJAYsCSQJpAiiCGIIIggACFBOAARABHwDRAFECEQI9A9EAEQAQgFCAgAEUk4AAAAJAAn8BRQFFAEUARQB8gESARQBAAFTTgAAAAEAAfwBFAEUARQBFAPyAxIFFAkACVROAACQAJQIVAQ0AxwAFAAyD1IAkgCQAAAAVk4IAkwBTAHsAwwA/A8MAAwA6gFKAioCSAFXTgAATAlMBfwFTAP8D0wDSgP6BUoJSAkAAFhOCAksBawE7AIMAfwPDAHqAioFKgWICAAAWU4AAAQDhAREBCQEFAQUBAwEBAQABAADAABdTgAIEAgQBpABfgAQABAAEADwBwAIAAgABl5OAAAwAAgEJwolCaQIZAgkCCQIBAgEBgAAX04AAEAAIAD8ByAIEAj+CxAIEAkICfgEAABgTgAAAAAEAhQCFAEkASQBhAiECAQI/AcAAGFOAAAACJAIkAjYBLQEsgKQAogBiABAAAAAZk4AAEAASABIAEgA/g9IAEgASARyBMQDAABwTgAEpAikBJQElAKEAfQAhAKEBJwEhAgAAHFOEACUD5IE/gSSBJIHAAD+BwAIAAgACAAGc04EASwBJAmqD2IBLAEAAP4HAAgACAAIAAZ+TgAC9AK0Ar4PtAL0ABAELguoCGgIKAgIBoBOAADkB7wCvwK+Av4Hvgq+CrwKvArgCwAGgk4AAMQPTAD6BfIH1gDWDwAA/gcACAAIAAaGTgAAAAAEAAQIBAgECOQHFAAUAAwABAAAAIhOAAAgACQAJAAkCDQI9Ac0ACwApABkACAAiU5QAFABWAFWCVYJ9AdUAVwBVAHwAUAAQACLTgAAhAC8ArwKvAr+B7wCvAK8AvwDhAAAAIxOAAAABAgECAQIBAgECAQIBAgECAQIBAAEjk4AAEAARABECEQIRAj8B0QARABEAEQAQACPTgAAIAAkAOQBJAEkCSQJJAkkCSQHIAAAAJFOAAAgACQEJAekBGQEJAQkBKQEJAUkDiAIkk4AAAQIBAjECTwJJAkkCSQJJA/kCAQIBAiUTgAEBAREBEQH/AREBEQERATEBwQEAAQAAJVOgAiICIgG/gGIAIgAiACIAP4PiACIAIAAmk4AACQExAQEBPwHBAQEBPwHBATEBSQEAASbTkAIfAhACX4JSAlICQAJPglICUgJRAkgCJxOAAjkCSQJJAn8DyQJJAn8DyQJJAnkCQQInk4ACOQJJAkkCTwPBAgECDwPJAkkCeQJBAihTgAACAD4BwgICAgOCAgICAgICAgICAgAAKRORAgkBBQEVAKEAgYBhAJUAhQEJARECAAApU4AAAQJJAUkBbQErAJnAiQBFAPEBEQMBAimTggBiAloBAgC+AEKCAoI+A8IAGgAiAEAAKdOAAzkAyQANAA0ACYAJAA0ACQAJAAgAAAAqE4EAIQAvACsCKwIrgiuDqwBrAG8AIQABACpTgAABAD0D5QElAT3B5QElASUBPQPBAAAAKtOBAKEAvQC1ArUCtYH1APUA9QC9AIEAgACrE4ABAQE9AKUCJQIlgeUAJQAlAL0AgQMAACtTgAAxAFEAHwBbAluCW4HbAFsAXwAxAEAAK5OAACECbwIrAesAa4BrAGsB6wIvAiEBQAAsk4AAEAASAVYA3gLSgnMB0gBaAVIBUgFQACzTgAAxAJ8A2wDbANuB2wL7ArsCnwKxAoAALpOAAgABAAEAAPAADwA4AAAAQACAAQACAAAv04gABAA/g8AAAQDhAREBCQEFAQMBAQDAADATkAAIAD4DwYAIAAgACAA/g8gACAAIAAAAMFOQAAgAPgPBgAABAgECAQIBAgECAQIBAAAxU5gABAA+A8GAAAIPATEAgQBxAI8BAQIAADGTkAAIAD4DwYAAAAAAP4PAAAwAEAAgAAAAMdOQAAgAPgPBgAQDBAD/gAQABAA8A8ACAAGyk4AACAAoACQAIgApACiAKQIiAaQASAAQADLTiAAIAgQBMgDBAACAAIABADIDxAAIAAgAM1OAAAgAP4PAAAED/wABAAECDwIIAzgAwAAzk4ACAAMwAM+AEAIgAwAAsAB/gAAAwAEAAjPTkAAIAD4DwYAAASAB3gEBgQABMAFAAYACNFOAAAgABAA0AcICYQIhghICFAIEAYgAAAA004AAEAAIADwBygIJAgmCSgJ8AgQCCAGAADUTkAAIAD4DwYAQABECEQI9AdUAEwARAAAANVOQAAgAPgHBgAgBCAEIAT+ByAEIAQgBAAA1k5AACAA+A8GAEAA/AcgCBAI/gkQCAgJ+AjXTiAAEAD8DwIACAhoBIgCiAN+AggECAQICNhOQAAgAPgPBgAQAFAAkAkQCBAI/gcQAAAA2U5AACAA+A8GAPAPAAQABP4HAAQABPAPAADjTkAAIAD4DwYAIgAgACAAfgCgAxIEFAgUBuROAAAQABAASABIAUQCUwJEDkgFyABQABAA5U4AAAAE/gMAAgAJDAkQBAAC4AEeAgAMAADqTkAAIAD4DwYAAAg8BMAEDgOAAnAEDAgACOxOAAAgAPwPAgDwDwYACAACAAIIAgj+DwAA7k5AACAA+A8GAAAM/ANkCKQFJAYkBeQIBAjwTgAAIAD+DwAA/AMCAgAA/A8EAAQC/AEAAPJOIAAwAPwPAgD4AYgAiAD+D4gAiAD4AQAA9k5AACAA/A8CALAAjgCIAP4PiACIAIgAAAD3TkAAIAD4DwYAEAzIAwQAAgAEAMgPEAAgAPtOYAAQAPgPBgBACEQIRAj8D0QIQghCCAAA/U5AAGAA+A8GAGAIWAbEAUAIRAiYByAAAAD/TkAAYAD8DwIACAwIA/gASghKCEgIyAcIAAFPIAAQCNAPCAgICAQI8g+ECIgIkAggCCAICk8gABAA+A8GAJAMlAL8AZQAlACUAPwBEAANT0AAIAD4DwYAQAREBvQFTAREBMQHBAQABA5PIAAQAPwPAgAoDGgEqAM+AigD6AQoDAAED08gABAA/A8DABAMEAKQAX8AkAESAhQEEAgQT0AAIAD4DwYAEAwQBD4C0AGSBlQIVAgQBhFPQAAgAPgPBgAQBpABUAD+D1AAkAAQAwAEF08AACAIEAbIASgBCAsGCAgHyAEQAiAEIAgYT0AAYAD4DwYAEAyQA34AEADQDxYIEAYAABlPQAAgAPgPBgBACDgEAAP+AAADQAQ4CAAIGk8AACAAoAiQCIgGpAWiBKQEiASQBqAIIAAdTyAAEAD4DwYAIAgkDuQJJAgkCCQLJAQgCB5PEAEIASgBRAEEAfIPBAFEASgBCAEQAQAAH09AACAA/A8AAKgAqACoAP4PqACoBIgDAAAgT0AAIAD4DwYAIACkAPwCpgSkCqQJpAAgACRPQAAgAPgPBgBQCEgETgPoAEgISAjIBwAAJk9AACAA+A8GACAA0AcICYYIiAhQCBAGIAAqT0AAIAD+DwAAFAzQAz4AkAgQCRAM8AMAAC9PQAAgAPwPAgD4D4gEjASKBIgEiAT4BwAAME9AACAA+AcGABAAkA+QBP4EkASQBJAHEAA0T0AAIAD4DwYAIAEsASAB/g8gASgBJAEAATZPIAAQAPwPAwAQAEgBVAJTAlQNyABQABAAOE9AACAA/A8CAPgDKAEoAf4PKAEoAfgDAAA6T0AAIAD4DwYAEADUB1QCVALUCwQI/AcAADxPQAAgAP4PAAD8BwACPAgABsABPgMADAAAPU8AACAA/A8CAOgHHgD4DwAA/A8EBPwPAABDT0AAIAD+DwAA/A9EBEQE/AdEBEQE/A8AAEZPQAAgAPgPBgAACPwJJAkkCSQJJAn8CQAIR09AACAA/A8CAFgASAhICMoHSABIAFgAAABIT0AAIAD8DwIASADoA1gATgDoD0gASALIAU1PIAAgAPwHAgAIBGgEiAUKBAgG6AUIBAAATk9AACAA/g8AAPwLJAokCiQKfACiByIIIAZPT2AAEAD8DwIAiAiICIoI/A+MCIgIiAgAAFBPIAAQAPwPAgAIA+gIXghICMgPSAhICAgIUU8gABAA/A8CAIgA6A9eBEkESARIBMgPCABTT0AAIAD8DwIACAHIACgC/g84AsgACAEIAlRPQAAwAPwPAgCAD4AEgAT+BIgEiASIDwgAVU9AACAA/g8AAOQDJAEkAeQJBAgECPwHBABZTyAEoASwAqgKpAjiD6QAqAKwApAEoAQAAFtPQAAgAPwPAgDoCKgG/gGoAP4PqAS4AwAAXE8gABAA+A8GADAACAAOAPgPKAEoASgBCAFgT0AAIAD8DwIAEAPOAAgI+A8IAMgAGAMAAGNPQAAgAP4PAAD8ByQBJAH8DyQBJAn8BwAAaU8AACAA/g8ABv4BwgHqDzoAqgNCAP4HAA5sT0AAMAD+DwEAJAOkAP8HJAk0CSwJIgUAAHNPIAAgAPwPAgAgCSQJJAm/DyQJJAkkCQAAdU9gABAA/A8CAIgIiQT6A4gAiAD6D4kAiAB/T0AAMAD+DwAA9AqUCpQE/geUCJQI9AgAAINPQAAwAP8PAAA+DKIDIgCiDyIAogc+CAAAhE8AAGAA+A8GACAJNAksCaQPJAk0CUQJAACGT4gIiARoBAgCCAH+D4gByAJoBIgEiAgAAIhPIAAQAPwPAgAoCaQElARWBVQCTAHEAAAAi09AACAA/A8DAGAMngLyAQAA/AkACP4HAACNT0AAIAD8DwAAqACoBqgAvAioCOgHqACgAJtPIAAQAPwPAwCIDP4CiACIAIgA/gKIBIgInU8gACAA/A8CAIgIyA8oBDoEyABIA2gEIAigT0AAIAD8DwIAiAioBIgC/gGIArgEmAgACKFPQAAgAP4PAADkDyQE/AckBPwHJATkDwAAo08gABAA/A8CAMAPXgRSBFIEUgReBMAPAAClT0AAIAD8DwIAqAikBKYDvAC0D8wIzAggBKZPIAAgAPwPAgDwCRAEEALeARQEFAT0BQAIp08AAGAA/g/4BOQDBAD8DgAA+AkACP4HAACoT0AAIAD4DwYAUAw0AxwAFAAyD1IAkAAAAK5PAABAAPgPBADQA6wCqALoC6gLqAboA4AAr08AAGAA+A8EABAJVAVUA9QBVANcBVAJEAm1TyAAEAD8DwIAYAiuCK4FrgauBq4F7ggACLZPIAAQAPwHAwDAB14EUgRyBFIEUgTeBwAAv08AACAA/g8AAPwIrAasBvwFrASsCPwIAAjCT0AAIAD+DwAAlAyUAuwApA+SAIoC4gwACcNPQAAgAPwPAAC8ByQEJATkDyQJJAk8CQAIxE9AACAA/g8AACQJ/A+kACAE/gIgBawICAbKT0AAIAD8DwIASAooCZ4FiAb4BswFmAgQCM9PIAAgAPwPAgDwDxYAUAFeAVAJEAn2DwAA0E8AAGAA+A8GAKQB/A+kAAAA+AkACP4HAADXTyAAEAD+D8AAzA+iCJkImAiiCMQPyAAAANhPAAAgAPwPAgAoASQBJAmqD2IBagEKAQAA3U9gABAA/w8AAJ4MkgKSAfIPkgGSAp4EgAjeTwAAEAjQB9gC1ArUBxIA1AMYCNAHEAAQAOBPQAAgAPwPAgCICWgECAP+AYgCaASICAAA4U9AACAA/A8CAKgOqAqqCqoKqAqoCqgOCADjTyAAYAD8DwAAXAlUBVQD1AFUA1QFXAkACelPAAAgAP4PAAD0DxQB/AAUA/wAFAj0BwAA7U8AAGAA+AcGAJAEKAUkBKYEKAaQBSAEIADuT0AAIAD+DwAA/AMQCEgKtgpkCTwFpAQkAu9PAAAgAP4PAAD8BwQA9A+GACQI9A8kAAAA8U8gABAA/A8CAAAJ/gVWBVYBVgX+BQAJAADzTyAAEAD8DwIAKAkoBf4DAAD+DygBKAEAAPVPQAAgAPgPBgBQCVQP1Aj+CFQDVAVUCUAI+k8AAEAAMAD+DwAA/AOmAuUHrAr0CyQIRAQGUEAAIAD4DwYA8A+UARQC/A+UARQI9AcAAAlQEAgQB+gAuA68DroKvAq4CrAK8A4wAAAAC1BAACAA/g8AAPwPBATUBXQFVAWEBPwPAAANUCAAEAD4BwYAIACsD6QEpgSkBKwEpAckAA9QQAAgAP4PAAD8AwAISAk2BaQDNAVMCUAJEVCAAEAA/g8AAPwPVAB8AAAAfAhUCPwHAAASUAAAIAD+DwAAvASkB5QCAAL8CAAI/gcAABRQIAAwAP4PAAD+BwoA6g6KCOoPigjuDgAAGFAAAGAA+A8GAOAPLACgA74CoAsoCOQHAAAZUAAAYAD8D+ABAAiUCFQFVAXUA1QFXAkQCRpQIAAQAP4PAAC0B7QCrAKuCywI9Ac0AAAAH1AgABAA/AcWABQA3gdUBVQFVAVeBdQHBAAhUEAAIAD+DwAA/gdqBWoFagVqBX4FwAcAACRQIAAQAP4PAAD0DwQE/AVeBVwFXAX8BQQAJlCAAEAA/A8AAFQB1AdwCVwJUA3UD1QJQAIpUCAAEAD+DwAA/A+8ArwCvgK8CvwPFAAAACpQQAAgAP8PAAD+CJIHkQCAAJIPkgj+CAAEK1BAACAA/A8CANAPSAHUD1IB1A9IAdAPEAA2UEAAMAD8DwIAAAn+BaoFqgGqAaoF/gUACTpQAAAQAP4PAADcC1wIXAZeAVwEXATcBRAIPFAQABAA/gcAAPwHXAVcBV4FXAX8BwQEAAA+UAAAYAD8DwIA+AMAAPQJFATcAxQI9AsACENQAAAgAP4PAgD4BwQIvA28DbwKvAu8CIQIR1AAACAA/g8AAPwPpAIcCsAIVAVUB9wIAABJUEAAIAD4DwYA8Ae0Bb4FtA+0BbwF8AUABU5QQABAAPwPAAD8ANQP1Aj8CdQC1Ab8CIAIT1BAACAA/gMADvwBlAdUAdYHVAHUB1wBwAdaUEAAMAD8DwIAyA9+BMAPMAjuBIgHeAgAAFxQAAAgAPgPBgDAAPwA7AjuD+wAfADEAAAAZVBAADAA/w8AAP4HAgQoCaoJ/wuqCb4JCAh0UEAAIAD+DwAA/A1UAfwNAAD4AQAI/AcAAHVQIAAQAPwPAgD4C6gGqAauAqwGrAb8CwQIdlBAAEAA/A8AAPwP1ADUAvwD1ALUCPwHAAB3UAAAIAD8DwIA6A+0AvQPEgDUAwQI6AcIAH1QQAAgAPwPAgGIDMoAuAauAKgKuAngCIAHf1AAADAA/A8CAJgMvga4Bb4EuAWqB5oIAACAUCAAIAD+DwAAfAxUAlQB/gdUCFQLfAsABIVQQAAgAPwPAAB4AXgFeAF8CXgJfA94AQABjVAgACAA+A8GAFAIVAbUAVYBVAlUB1QAAACRUEAAIAD8DwIACAl2BQwBsA8sAX4FJAkAAJhQEAAIArgC9AICAvIPBAK4AqgCCAIQAAAAmVBAACAA/g8AAPQHFAD2D7QC9A+2AvQPBACiUEAAIAD+DxwATAO8Cn4JnAecAFQBTAYABKhQAAAgAP4PIADmBwAA1A9+BVQFXAXUDxIArFBAACAA/g8AAMwA+A+oCq4K+A+oCqwKIAiyUAAAQAD8DwAEqAP+CYgHwAD0DAoD+AQICLNQIAAQAP4PAAC8ArwGvAL+CrwK/A+8AwADtVAgABAA+A8GABAI/Av8Cv4C/Ab8BvwLEAi3UCAAEAD8BwIASAX+AvwB/AL8AfwExAMAALtQQAAgAP4PAAA8CnQLdAsuBWwNZAq8CIAIvlBAACAA/A/4ARACBAn8B6wGrAKsCvwLAADFUCAAEAD+DwAA9AreCtwK/A/cCt4K9AoECM1QAAAgAPwPCgBsCfwHagVKDBAD/gjwBwAAz1AAACAA/A8CALgFbgXuArwJLAeoATgCAATRUEAAIAD+DwAAnA/8A9wF3AXcB/oIqg8oANVQIAAQAP4PAABqCXgFbgPoAW4DeAVqBQAJ2lAQABAA/A8CABAE9AK8CLYPvAD0AhQEIADnUAAAEAD+DwEA/A9tBWYFfAVsBecPPAAAAPVQAABAAPwPAAD+D9YP1g/+D9YP1g/+D4II+VBAACAA/g8AAPoL6gf+B+oD/gfqB/oLAAj7UAAAYAD8DwAC/A+kBLwHAABYAcwPaAFIAABRQAAgAPwPAwAoC6sPagM+CaoHKwWqCSAJAlEAAGAA/g8ABPwD7A/+BewB/gPsBfwJQAAEUTAACAD/DwAE+gJaAVoNWwlaC14J+gEKDAtRIAAgAPwDEw74AQ4E2wfKB84H2gfoB2gAElEAACAA/g8AAHoPSgHeD0oBWg9qAUgPAAAYUYAAYAD8D4MAqA2qCqoP/wqqD6oKvg2ICR9RQAAwAP4PAQDMC/8L7AvuA+wL/gvMCwAAIVFAACAA/geAD/4H6gO+D2oF6gd+BcAHAAAqUSAAEAD8DwMAogo+Cb4HvgW+Bf4HIgVgCDJRAAAgAPwHAgBYD1oFAAbUAP4HWAXWBwAAP1EACAAMAAL8AQAAAAAAAPwHAAgACAAIAAZAUQQIBAQEA/wABAAEAAQA/AcECAQIBAgEBkFRAAhACCAEMALsASIAIADoBygIMAhACAAGQ1EAACAIJASkA2QAJAAkAOQHJAgkCCQIIAZEUQAAAAh8BEQCxAFEAEQAxAdECHwIAAgABkVRAAAICEgIaATYA0oASgDYB1gIaAiICAgGRlGAAAQJmASAAv4BAAAAAP4HQAiQCIwIAAVHUQAAAAh8BEAE0gNMAEgA1gdCCPgIAAgABkhRQAhQCE4EyANIAH4ASADIB0gISAhABgAASVEgCCIELALgASAAPwAgAOAHKAgkCCIIIAZLUQAIBAj0CJQGlAGeAJQAlAeUCPQIBAgEBk1RAAAwCPgIlASWApQB9ACcB5QIkAjwCAAGUFEAAAAIfggABv4AkgCSAJIGkgj+CAAIAARRUQAAAAj4CIoEjAOIAIgAjAeKCPgIAAgABFJRAAAACHwIVATSA1IAQADUB1QIfAgABgAAVFEAADAI8AicBJYClAH8B5QIkAqQCvAKAAhWUQAAJAiUCNQEpAOlAIYArAfMCJQJJAkkBFpRAAA4CAoI7ASoA64AqACoD+wICgg4BgAAXFEACPgERAQAAnwBVgBVAHwGgAh8CAAGAABiUQAA9AyUA54A9AcAAvQIlAeeAJQP9AgABmVRAAAACAAEBAKEAXQAPADAAAADAAQACAAIZ1EAAPAPEAASAdIAPgBQAJAAEAkQCPAPAABoUSAIEAkICSwJIgniDyIJJAkICRAJIAgAAGlRAAAAAPQPFADUARQA/A8UANQBFAj0BwAAa1EACAAGwAE0AAQABAAEAHwAgAMABAAIAABsUQAAIAQQBAwGggVgBCAEAASCBAwHEAwgCG1REAgQBBADkAAQAB4AEACQABABEAYQCAAAblEgABAAHADyAJAIkAiQCJMMhAMYACAAAABwURAEkASSBJQEkASQBJAEmASWBJAEEAQAAHFRgAiICIgE/gKIAogAiACIAv4CiASICIAIc1EAAEAESARKBEoCSAH4AEgBTwJJAkgEQAR0UYQImAigBIAChgCYAIAAgAKwAogEhAgAAHVRAAAACQAJ/AUUBRQBFAEUA/IDEgUUCQAJdlEAAAQJBAX+BVQFVAFUAVQD/gMEBQQFAAV3UQAJAAX+BVYDVgNWAVYBVgNWA/4FAAUACXhRAAn8BSQFJAP+AyQBJAH+AyQDJAX8CQAJeVEAAIgIyAa6BYwECA6IAMgEvAeKBEgGCAh7UQAAIAE4CboE/AM4ADgA/A+6ADgBKAEAAHxRQAhYBVoFXAP4D1gBWAH4D14D+AVICAAAfVEAAIAA/A/VBdYF/AXUBdYF1QX8D4AAAACAUQACjAr8CrwH/gbwAvYC/Aa8B/wKiAoAAoVRAAD4DwgACAOIAMgAPgBIAIgJCAj4DwAAhlEAAPwPRABEAEQAfABEAEQARAhECPwHAACIUQAA/A8EABQCJAHEAMQANAEECgQI/AcAAIpRQAD8D0QARAD8D0QARAD8D0QARAj8B0AAjFEAAEAM/ANECPwHQAz8A0QIRAj8B0AAAACNUQAABAH0D1QBVAH8AVQBVAlUCfQHBAEAAJJRAAAGAPIHXgVeBV4FXgVeBV4F8gcGAAAAlVGACIAIjAvkCuwG7APsBuwKjAqcCwAIAASXUQAIHAgEBOQDJAAkACQAJADkBwQIHAgABplRAAAMAQQBZAFUAVQBVAlUCVQExAMMAAAAm1EAAAQClAL0ApQClALUD5QClAKEAgwCAACcUYAAjABECMQHNAQMBHcChABEASQCLAQABKBRgAisBKQDpACkBwQIRAlECUQI5AtMCAAEpFEAAGwI9AksBSwD7AE8BywJJAvkCQwIAASlUQAFBgl6BVoDWgPaAdoBWgNaA3oFDgkACahRAAAOAIIP+graCtoP2graCvoKgg8OAAAArFEAACABkACIBEwEVgUkBVQJTAlECIAAgACvUQAABAaYAQAABAF0AUQBRAlECXwMwAMAALBRAAAEB8QAEAYQAfAIAAj+B0AAoAEYAgAEslEABAYG2AEAAPgBiACIAP4PiACIAPgBAACzUQAABAaIAZAIAARIAkgB/gDIA3gEQAgAALVRAgQGA8gAAAh+BEICwgFCAMIHQgh+CAAGtlECBAQDiAAgALAPrASiBKAErASwByAAAAC3UQIEAgKMASAAmACEAJIPkwCUBIQEiAcQALtRAgQMA4AABARkA1wJRwj0D0QARAFEBgAAwFEAAAQEmAOAABAAXAlWCfQHXAFUAfABQADEUQQMhAMUCFwJXAvcDX4FXAdcCXwJNAkAAMZRAgQcA8AAEAD8D6oEqASqBP4HqASoBAgEyVECBAwDkAAEBPQClAiUCJYPlAD0AgQEAADMUQAAhAcYAEAKVAo0CbQGngS0BrQFFAgACM1RAAIMA8AABAT8BKwCrAH+D6wBrAL8BAQEz1EEDhgBAAz4AwgAqAeoAggNfgKIBeoIAAbRUQAAAgbMAQAEfAUcBVwD3gFcBTwFXAVQANtRBAKEAUgAAAX8BcwB/AX+B/wBzAP8AwQF3VEEBMQDAABeCTQF8gMqDAAHTATMD1wJRAngUQAIAAgABvwBBAAEAAQABAD8BwAIAAgABuFRAAAACAAH/AAEAGQAhAAEAPwHAAgACAAG5FEAAAAO/gECBFIEkgKSAXIGAgD+BwAIAAbmUcAIMATOBogDeAQABfwIBAgECPwLAAkAAOtRAAgACDwEpAOsAK4ApACkByQILAngCAAE7VEQCBAIPgSAA9QA1ADUAPwA0gdSCFIIAAbvUQAAPA8wBT4FMAX8AQAM/AMEAPwHAAgABPBRAAAADPwDBAj0CvQP9Ar0CiQA/AcACAAG8VEACHwJuAS+BLgH/A0ABPwDBAD8BwAIAAfzUQAIkAiSBLoD/gG6AbgB/AG6B4gIlAgQBvZRAAD4BwAEBAWIBFAEMARIBIYFAAT4BwAA+FEAAOAPIAQgBDwEBAQEBDwEIAQgBOAPAAD5UQAA/A8EBAQEfARABHwEBAQEBPwPAAAAAPpRAACADzwEIAQgBP4HIAQgBCAEPASADwAA+1EAAEAASAdIBEgESAT+B0gESARID0AAAAD9UQAA+AcCBJIEkgZCBvoHSgSmBQIE+A8AAP9READwB/QHEAReBdAFUAVeBRAE9A/wDxAAAFIAAAQIBAQEAoQBfAAEAAQIBAgEDPwDAAABUgAAAAAEAgQBhABEAEQEJAgECAQM/AMAAANSgAjECDQEBALEATwABAgECAQIBAz8AwAABlIAACAIEAgMBCID4AAgCCIIJgjIBxAAIAAHUgAAIAD+AxAClAkEBIQDfAAECAQI/AcAAAhSBAQIAjABwAA4AQYCAAD4BQAIAAj+BwAAClJAAEQARAD8D0QARAAAAPwJAAgACP4HAAARUgAIRAz8A0QARAD8D0QAAAD8CQAI/gcAABJSEAgQBD4C0AOWBFQIEAYAAPwJAAj+BwAAF1IAAMQIJAicBBQD1AA0AAAA/AEACP4HAAAYUggIKARKAswBOAIIBAAA/AEACAAI/gcAABlSAAAACPwIBAb0AQQE/AkAAPgJAAj+BwAAGlIAAP4PAgKyAfoJAgj+BwAA/AkACP4HAAAbUgAAEADIByQIJgnICBAGAAD8CQAI/gcAAB1SCAGIAO4PmABICQAEhAN8AAQIBAj8BwAAIFJADP4DQgj+B/wPQgD+DwAA/AkACP4HAAAkUoAArACgAP4PoACsAAAA+AEACAAI/gcAACVSAAg8BuQBpAikCLwHAAD4AQAIAAj8BwAAKFIgAPAHrAioCOgJCAn4BAAA+AEACPwHAAApUhICkgFSAP4PUgCRAQAA/AEACAAI/gcAACpSQAD+D0IA/g9CAP4PQgD+DwAA+AkACP4HK1IACLwIpAakAaQIvAcAAAAA+AEACAAI/AcuUhQAlA+SBP4EkgSSBxAAAAD8CQAI/gcAADBSBAi0BKwEpAekBJQEpAQAAPwBAAj+DwAANlIgAKwHqgCoAP4PqACoBKgDAAD8CQAI/gc3UuAPHADUB1QA9A9UANwHAAD4AQAI/AcAADhSIAGqCOoIrAS4Aq4BqAi4CKwG6gGqACABOVJQBFQDVAjIB0wAUgFSBgAA/AkACP4PAAA6UgAEdAYUAf4PFAH0AgAA/AEACAAI/gcAADtSAAAkCbQErwJkAhQDxAQAAPwJAAj+DwAAPVIgAJAMiAakBaYEiAawCAAA+AkACP4HAABCUkQIVARUAzYANACsDyQAAAD8CQAI/wcAAENSAAToBKoCrAH4D64AuAcAAPwJAAj+BwAAR1IACP4FKgUqASoB/g0AAPwBAAgACP4HAABKUgAA9g9QAV4JUAn2BwAA/AEACAAI/gcAAE1SAADoD6oCrAqoCugHCADoAw4I6AcIAAAATlIAAFIEUgNMAOQPSgJqBAAA/AkACP4HAABRUhAAWAiEBRIE0wQEB8gCAAL8CAAI/gcAAFRSAAG+BOoCqg2qA6oIvgcAAPwJAAj+BwAAVlIkAKwPrASmBKQEvASsBwAA/AkACP4PAABbUgAA/A8MA+QDFAn8BwAA+AEACPwHAAAAAF1SAADAANgKVgrUB3QAzAYAAPgJAAj+BwAAY1IQCPAEvAbyAbQC5AwAAPwBAAgACP4HAABkUgAAJAisB5YCrAKkDyQAAAD8AQAI/g8AAGVSoAIqCioJ6g8qAb4CoAYAAPwJAAj+BwAAZ1LABz4AqgeqBPoEqgSuBwAA/AAACP8HAABpUogIbARsAgwB/A8MAWoCKAQAAPwJAAj+B2pSAAV8BTwFPgW8A/wBBAF0BQYFdAUEAwAAb1ICAN4PVgXWB1YFVgXeDwAA/AkACP4HAABwUgAArAT8AqwB/A+qAvoGAAD4AQAI/gcAAHJSjAC8D7QFtAX2BbQHtAeMBwAA/AEACP4HdVIQABAO0AP4BfQF5AXoBwAA+AEACPwHAAB/UgAIdgVwBXQD+g94A3ADdgUAAPgJAAj+B4NSSAT8B/wH/gf8B/wH/Af8BwAE+AEACPwHh1IADvgBGATYBn4DbA9sAggE8AEACPwPAACIUoAAfAj0CbQJtAX4A1QBXAn2CVwJVAcAAIlSAACcCcoLyQmwB84FQgUOBPgBAAj+BwAAjVIICOgErALqAEsIogbkBAAA/AEACP4HAACRUjgASAz0A4wClgK8AswPIAD8AQAI/gcAAJtSAAAQCBAEEAKQAX4AEAAQCBAIEAzwAwAAnVIABBQCZAHkABwJAASIA34ACAgICPgHAACeUgAAiAloBAgCiAF+CAgICAz4AwAA4AEAAJ9SBAIEAvwBBAkECRAEEAP+CBAIEAzwAwAAoFIICAgG/gEICAgI+AcAAPwPBAQEBPwPAAChUkAIUAlICSwFNgOkASQJNAlMCUQHQAAAAKNSKAioCKQIogSgA54AkAiICIIIhAcIAAAAqFIAACQE5AMkAqQCJAMACBAH/gAQCPAHAACpUgAE/gOSBJIC/gsABBAD8AgfCBAI8AcAAKpSAAhECVwJZgUkA1wBRAE0CSQJXAdEAAAAq1IAAEgHyAR+AkgCQAcQABAP/gAQCPAHAACxUgAO/AEUDPQDVAjUBwQAEAf8ABAI8AcAALJSAACkCKQElAesBKQEMAAQDv4BEAjwBwAAs1IACLQIlASWBJQD1ACUCJQIlgiUBzQAAAC0UgAIuAiKBIoEiALrAYgIiAiMCIoHOAAAALlSKAioBKgCDAGoAggIUAQQA/4IEAjwBwAAv1IAABQIVAl+BRQFwAM0AR4JBAk8B0AAIADBUgAAtASEBLQHhAS0DAQEEAP+CBAI8AcAAMNSAAA0AhQJXgeUATQJAAQQA/4IEAjwBwAAx1IACAIK+gqqBqoC+gKqAq4Kqgr6BgAAAADJUhAI+ASWA/QAnAfwCBgKmAl+CAgK+AkABMtSAAjuCSoEqgMqCO4LAAAQB/4AEAjwBwAA0lIAAOQCvgL0D74C5AgEBBAD/ggQCPAHAADVUgAAfAl8CfwHeg0IBBgCkAF+CBAI8AcAANhSgACCB/8FqgSqBP8FggwIAv8BCAT4AwAA2VIAACoDqgjqBzYA4AlIBy4BNAk0CSwHRAHdUgAM/gOSCP4HgAFqCTgHrgE4CW4JqgcAAd5SEgiSCKgErgSgAqIBpAioCKYIKAdyAAAA31IAAkQB/AV+A3wDfAF8BX4F/AN8AUQCAADiUgAArAmsCe4FrAWAA/gBGAkOCXgGgABgAORSAAj0CdYF9AfWBfQFAAwQA/4IEAjwBwAA51IAACAB7A+4B+gHqAeoDgAG/AEQCPAHAADyUgAB/g3+Af4B/g3+AQgLmAh+AAgB+AwAAPNSBAh9BW0B/wTsAvwCAAaIAX8ECAT4AwAA9VIADPwDzA/8AewD/AvMDwAM/gMQCPAHAAD4UgAB+g+7B4AHuwe6BwAM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JAAAAK1T+AcEAPQD/AJ8B3QFhAckBPwDJAEUAgAErlMABv4BAgT6AVoD+gECDPoDSgDKD0oAAACyUwAO/gECAOoP6gHqBeIH6gXqBeoJCg8CALNTAA74AQ4E+Ae4BroG/A8IAOgMLAPqBSgIu1NAAEgMSAZIBcgEfgRIBEgFSAZICEAAAAC/UwAAgACABP4GqgWqBKoEqgSqBP4GgASAAMFTIAGoCKgK7Aq6CqgKqgruCqgKqAggAQAAwlMgASgBqAhsCroKqAooCWoErASgACABAADDUwAAAAGwAKQJdgulChQKJgl2BKAEMAFAAchTAAAACAQIHARkBIQCBAGEAmQEHAQECAAIyVMACAQIPAREBJQCFAGUAkQEPAQEBAAIAADKUwAEBAaEAXwIJATEBAQDHAOQAnAEEAQAAMtTAAQIBAgDyAh+BKgEKAMoA6gEaAQICAAEzFMUCDQERALkARwKAAh8BIQDxAI8BAAIAADNUwAAAAz8AyQI5AgkBSQCJAakBWQIBAgAAM5TAAL4AQABAAH+DwAAFAjkBAQDxAQ8BAQI0VMABBACHAuQCPAIXgVQBlAF1ghQCBAIAADUUwACoAEgCP4PJACgCwQIfAaEAeQCHAwACNZTBAT8B5QElASUAvwPAABoCIgFCAPoBBgI11MAAHQIHATcBFQFXAJcAlIF2gQaBDAIAADYUwAIpAiUCIQJvAaGBIYEvAaECZQIJAgACNlTUAZYAVQI8g9UAFQDBAh8BIQDxAI8DAAA21MAAKwIoAb+AaQAAA78AXIMkgOSAnIMAATgUwAA6Ah2CFYPVg9GD1YPXg9WD3gIyAgAAOFTAACoANgHvwfaB/oHkAAMDPQChAF8BgAE4lMIAKoE7Af4B/wH6A/oAPwH7AXqB4gJgAjjUwAAAAD8DwQEBAQEBAQEBAQEBPwPAAAAAORTAAAIAIgPiASIBP4EiASIBIgEiA8IAAAA5VMAAGAAEADMByoCKAIoAugLCAgIDPgDAADmUwAAgAi8BKQEpAPkAKQApAikCLwIgAcAAOhTAAD8AwQB/AkACAQGhAF8BAQIBAz8AwAA6VMAAPwDBAEEAfwBAAD8DwQABAIEAvwBAADqUwAAAAh8BEQCRAFEAEQARAFEAnwEAAgAAOtTAAD8AwQBBAH8AQAA/AMAAQAB/g8AAAAA7FOAAEQARA8kCRQJ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ggCyAIoAAAB9VAAAEADQB1gCWALUAxIA1A9YAEgC0AEQAIZUAAD8AwQB/AEwAPgDlgSUBPQFBAX8BAACi1QAAPwDBAH8ARAACAAOAPgPKAEoASgBAACMVAAClAFUAPwHUgAAAPwHBAIEAgQC/AcAAI9UAAD8AwQB/AQAAtQBFAj0D4QAQAEgBgAEkFQAAPwDBAH8AWAA/A8CANAAEAkQCP4HEACSVAAIHAgUBNQDXABAAFwAVADUBxwIAAgABpVUAAD8AwQB/AEAAJAPkASQBP4EkASQDxAAllQAAPwHBAH8DAAC/gkIDPAD/A8EBPwPAACZVAAA/AMEAfwJAASQA34IEATyB5YIVAgQBJpUAAD8AwQB/AFQAEgCLgK1BDQFTAlEAEAAplQAAPwDBAH8AQAI7ASsAv4BrAKsBLwJAAinVAAA/AMEAfwBQAi8BJQDYAD4CQAI/gcAAKhUIAARAIoPoASsBKMEkgSOBJIEog8uAAAAqlQAAPwHBAL8AwAIrAcgAP4PIACoAyQMAACsVAAA/AMEAfwBAAgoCKgECgPMBQgEaAhACK9UAAD8AwQB/AEQAYgPVgllCXQJXAmED4AAsVQAAPwBBAH8AQAA+AeoAqwCqgKoAvgHAACyVAAA/AMEAfwJAAhOBEwC+AFMAk4ESggAALNUAAD8AwQB/AEACCQFtARvAiQClAVECAAAuFQADPgDCACoB6gCqAoIC/4ICAfKCCwIAAa9VAAA/AMEAfgA/g8CBCIF+gSiBQIE/g8AAMBUAAIEAXQBVA/UBFUE1gRUAVQCdAUEBQAIwVQAAMAPQAReBNIHEgDSD1IEXgRABMAPAADEVAAA/AMEAfwBAAiIBP4CiACIAP4CiAyACMZUAAD8AwQB/AEACCgJpASWBFQCTAHEAAAAx1QAAPwDBAH8AQAIJAkkCb4PJAkkCSAJAADIVAAA/AMEAfwBYAAwD6gEpgSoBDAPYABAAMlUEADUB1QCXgJUAtQJEAz+AhADkgRUCAAHzVQAAPwDBAH8AQAA+A8IAOgDLgHoCQgI+AfOVAAA/AMEAfwBAAgkCM4EBAMEBc4EJAgECNFUAAD8AwQB/AVABAQE/AcEBPwHBATkBAAA11QAAPwDBAH8AQAAfAECAZAPPgFIAUQBJADfVAAA/AMEAfwBAAT8BYIEEALOCAgI+AcAAOFUAAAACP8LrQatBq0CrQKtBq0G/wsACAAA5VQAAEQAfAdsBWwFbAVsB3wARAj8D0QAAADmVAAA/AMEAfwBAAj8D5IAEAz+A5AFVggABuhUAAD8AwQB/AEAAPYPUAFeAVAJUAn2BwAA6VT8AwQB/AEAAHwJVAlUCfwPVAlUCXwJAADqVAAA/AMEAfgMAgL+CZII/gf8DwIA/gMAAO1UgAieCJIIkgSeA8AAnAOUBNQE3AiACAAA7lQAAP4BggD+AIABagEqCb8PagEuASoBCQHyVAAAEgCyD78EigSgBJ4EiQSJBLkHCQAIAPpU/AMEAfwBAADoD6gCqAL+D6gCqgrsBwAA/FQAAPwBhAD8AAAAvAisCK4OrAGsAbwABAABVQAA/AcEA/wDAACoDqgKqgqqCqgKqA4IAAZVAAD8AwQB/AlACCgJngaIBPwGzAVYCEAIB1UAAAAP/gAiDuoKqgpqCqoKqgoqDSIBAAAJVQAA/AMEAfwBAAgoCS4F6gMoBS4JKAkAABBVAAb8AQQAXAdcBVwF/gVcBVwFfAcUAAAAFFUAAPwDBAH8AQAAqge6BK4EqgS6BKIHIAAgVQAA/AMEAfwBAAiUBJYC1AmUCJYMNAMAACRV/AMEAfwBAAj4BJQClgH0AZwClAT0BIAIJ1UAAPwDBAH8AQAE/AdUAjwA+A8EAPwDAAAsVQAA/AMEAfwBAAz4AygIPgdsAGwHTAgABC5VCAAIAHwPXgVcBVwFfgVcBVwFXA9EAAAAL1UAAPwDBAH8AQAA/A+qBKgE/geoBKgEAAAxVQAA/AMEAfwBAAD+B2oFagVqBWoF/gcAADdVAAD8AwQB/AEACPQH7ADmAmUKZAr0ByQAOFUAAPwDBAH8CAAGKACkBLIJNAr0ACQGCAg+VQAA/AMEAfwBAAjUChQK/A8UCtIKUAoQCENVAAD+AYIA/gAIAO4PqAKvAqoKqgrqDwAARFUAAPwDBAH8ASAElAJUCiwJxAdkAJQDAARGVQAAAAD0DxQAfAcWBTQFVAVcBxQA9A8AAEpVAAD8AwQB+AD+DzIAzAHkAxQB5Aj8BwQAT1UAAP4PKgCqB74CgAK+AqoDKggqCP4HAABTVcIAOgCqB6oEqgS6BJAEzgS0BDQHTACEAF9VAAb8AaoHqgKqAroDIAR+AogB6AIYBAAAYVUAAPwDBAH8AQAAKAH+DwAA/g8oASgBAABkVfwDBAH8AQAAfALUAtQCfgJUD1QCfAIAAGVVAAD8AwQB/AEQAEgHRAXyBVQFRAVIB1AAZlUAAPwDBAH8AQAM/gMICGAIiQkKDugJAABqVQAA/AMEAfwBgAj+B0AA+A+MBIoE+A8AAHBVAAD8AwQB+AmOCVIIfgVSAl4B0gFeAAAAeFX8AwQB/AEADKwDLAYsAf4PLAEsArwPCAB8VQAA/AMEAfwBAABsB6QApg+0AKQEpAMAAIBVAAD8BwQC/AMAAKQPtAlWCVQJpA6MAAAAglX8AwQB/AEAAPwA1A/UCPwJ1ALUB/wJAACDVQAA/AMEAfwA4A8UALQCngfUAhQI9AcAAIRVAAFEAVQPVgtUC/wLVAtUC9YL1A9EAQAAh1UAAPwDBAH8DGQCng/0AZQCYAj4CP4HAACJVQAA/AMEAfwBYAD4DwYAUAlUBdQDXAVQCYpVAAD8AwQB/AzwAwgHqAIIDX4CiAVqCAAGlFUAAPwDBAH8DAAC/Ak0CPQKtA+0CrwKAACYVfwDBAH8AQAAvA+wALAP/gCwD7AAvA8AAJpVAAD8AYQAfAjwBVgFNgOUAzwFUAXwCQABnFUAAQQBfA/cC1wLXgtcC1wL3At8DwQBAAGdVQAA/AMEAfwBAAC+BqoEqgWqCKoIvgcAAKdVAAD8AwQB/AEICPQLtAq2CrQK1AscCAAAqlUAAIQIvAisD7wIhAj+CYQCvASsCrwKhAirVQAA/AMEAfwJAAg+BTQDwAM8BQQJfAkAAKxVKAAsAJwP/ADcB9wF3AXcBfwHmgioDyAArlUAAAAE/gVaBVoF/g9QBV4FWgX6BQ4EAACyVQAA/gMCAf4BGAznApAAUAaOAAgI+AcAALNVAAD8AwQB/AEACNQLrAquCqwK1AsUCAAAtVX+AYIA/gAAAPAPkgSXBPIHkASXBPIHAgC2VQAAGAAKB3wFWAXeBVgFWAV8BQoHGAAAALdV/AMEAfwBAAjUC1QIVAZWAVQEVAjUCwAIu1UAAPwDBAH8ARgA2A+0AvIPlAEYCMgHEADFVQAA/AMEAfwBAAh8BVwFXgNeA9wFfAUACc5VAAD8AwQB/AEADP4AqgaqAP4EqgGCDwAA01UAAPwDBAH8AQAIUgV2A4YPlgNSBTAFAAjaVQAA/AMEAfwJAAT8ANQO1gDUBdwIwAcAANxVAAD8AwQB/AFAADQHfgfUB9wH1AfUBwAA4VX8AwQB/AEABMgCVAqiBxAAUgJ0CuQHCADjVQAA3A/UB9QP3A+EAVQC1AsECPwHAAAAAORV/AEEAfwBAATgB2wGeAbuB2gGaAbsDyAI5lUAAPwDBAH8AQAMVAH0CV4HFAGUBTAIAADoVQAA/AMEAfwAAA5EAJADzAKoC6gK6AcAAu9VAAD8AwQB+AA+AWICagRaBVoEQgF+AwAC/VUAAPwDBAH8BCQC/g9UAXQJEAbOAQgGOAgAVgAA/AMEAfwBAAD0DxQC9gVUBVwHFAj0BwFWAAD+AYIA/g74AYQE9AcUAIcEfAOFBnQCBlb8AwQB/AEACPQK1ArWBvQD1AbWCvQKAAAJVgAAgg6+Au4KrgqvBq4M7gquCr4Kgg4AAA5WAAD4AwgB8AUEBXwF/AN8A3wF/AV8BwQDF1YAAAwARg/WC/QL9gv0C/QL9gvFD5wAAAAbVgAA/AMEAfwI+AcEAfQPJgKEAfQPpAAEAx9WAAD8AwQB/ACAD34FUAWID/wHBAD8AwAAMVYAAPwBhAB8CAAH/A60AdwF/AfUAdwPAAAyVgAA/AMEAfwDfAKuD3wC/geSAP4PAAAAADRWAAD8AwQB/AEQBNgD7gL0B/AC7gLUBxAANlYAAPwDBAH4AAQN/gFUAf4N/AMkAOIPIgA5VgAA/gOCAf4BAAz6AVYJUw9WAfoBMgwAADtWAAD8AwQB/AEAAHwP3AteC1wL3At8DwQBP1b8AYQA/AAACF4FVgFSBf4JWgFSBV4AAA5BVgAA/AMEAfwAIA1UAdwHBAvcC1QJdAEADWJWAAD8AwQB+Al8CAwFZAN2ASQDRAV8CQAAaFYAAEACXA9UCdQJfA9gAEwP9Al0CVwPQAJpVgQIxAv8CvwLRAj8D0QI/AvsCvwLBAgAAGpWAAD8AwQB/AnABbwF9AOUD/QPvAPgBQAIdFYAAPwBhAD8BBAE9Af0AvYC9ALUBvQHEAiHVgAA/AMECfwH5As/COQPJA3kA/8PJACgA45WAAD8AwQB/AFACDwGvAa+A7wPvAPkBAAAo1YAAIgH7gWuBW4HWABuB24FrgVuBYgHAAC0VgAO4AG8BPQH9AfsD7gB1Al0BvwFYAgAALdWAAD8AwQB/AUADPwP7An+AfQH7AX8CQAAvFYAAPwDBAH8AQAA9A/yBfoMMgG5CLUHgADCVgAACAL+B/4H/gf+BvgA/gb+B/4HCAUAAclWAAD+AYIAfAH3BqUCBwP9D1cF9QdXBQAAylYABbQD/Av8B/wFngX8AfwD/AXcB5QHAADaVgAA/g8CBAIFggRCBDoEwgQCBQIE/g8AANtWAAD8DwQExAU8BAQEBAR8BIQEBAT8DwAA3lYAAP4PAgTyBJIEkgSSBPIEAgQCBP4PAADgVgAA/g8CBCIFIgWiBHoEogQiBQIE/g8AAOJWAAD+BwIEkgRSBFIFMgX6BRIEAgT+BwAA41YAAP4HAgQKBGoECgUKBfoECgQCBP4HAADkVv4PAgRSBJIEkgT6BZIGkgZSBgIE/g8AAO1WAAD+BwIEKgXqBCoE6gUqBSoFAgT+BwAA8FYAAP4PAgSiBWIE+gViBKIEIgUCBP4PAADxVgAA/A8EBEQFJAVXBZQEdAUUBQQE/A8AAPJWAAD+DwIEkgT6BZIEkgT6BZIEAgT+DwAA81YAAP4PAgZaB0IFygTCBDIFCgUCBP4PAAD0VgAA/g8CBKoEqgT6BaoEqgSKBQIE/g8AAPpWAAD+DwIEEgTSBVIFegVSBdIFAgT+DwAA/VYAAP4PAgQqBSoF+gUqBWoFKgUCBP4PAAD+VgAA/w8BBCkFLQUVBZUFrQYlBgEE/w8AAANXAAD8BwQE9AX0BPwE/AX0BPQE9AUEBPwHBlf+BwIG3gZWBlYFVgVWBF4GwgYCBP4HAAAIVwAA/gcCBD4E/gU6BroGegY6BQIE/gcAAAtXAAD+BwIE6gWqBUoEegWKBW4GAgT+BwAADVcAAP4HAgT+BX4FfgX+BX4FegUCBP4HAAAPVwAA/gcCBGoE6gX6BuoG+gZqBAIE/gcAABJXAAD+DwIM6g1qC3oLagtqC+oLAgz+DwAAE1cAAP4PAgjCC/oL6gvqC+oL+gsCCP4PAAAWVwAA/gcCBP4H9gf2B/YHfgbiBwIE/gcAABhX/g8CBP4E/gX+BP4E/gb+B/4EAgT+DwAAH1cABCAEIAQgBCAE/gcgBCAEIAQgBAAEAAAjV0AIRAhMCSwJNAmkDzQJNAlMCUQIQAgAACdXAAAADPwDBAhECEQI9A9ECEQIRAgECAAAKFcIAYgAyA84AI4IiAiICOgPiAiICIgIAAAtVwAIJAkkCSQJJAm/DyQJJAkkCSQJIAgAADBXAAIQAv4BEAFAAPwHIAggCP4JEAgQCfgEM1cABBAC/gMQAQAM/gMAAAAA/AcAAAAA/g86VwACEAL+AxABAAEiCfIEKgLmCSIM4AMAAD5XAAQQAv4DEAoABIQDfAiEBCQHPAXgBAAIQFcQAhAC/gEQAQAI8A8ACAAI/g8gCCAIIAhCVwACEAL/AZAEAAb+ATIM0gISA5ICcgQCCEdXAAIQAv4BEAEwAAwCKgEoCagICAz4AwAASlcQAhAC/gEQCQAMCAL4AUoISAhICMgHCABOVwACEAL+ARABQAg0CAoG6AEIAkgEOAgAAE9XAAIQAv4BEAFEAUQAJAD8DwQAJADEAIAAUFcgCJAIjgiwCIAI/g+gCJAIjgiQCCAIAABRVwACEAL+ARABAAzoAyoAKgDoBwgICAYAAFdXAAIQAf4BkACACAgESAP+AMgBSAJ4BEAIWlcACD4IAAkACX4JRg8qCRIJKgkmCUAIAABbVwACEAL+ARABAAQkBqQFZAQkBKQFJA4ACF1XAAIQAv4BEAEACP4JAgQCAvoBAgQCBP4JXlcQAhAC/gMQAQAC/AKEAoYCpgqkCJwHAABfVwACEAL+ARABgAkIBHgEigOMAngECAgIBGBXAAR+BQIFPgUCBaAHMAUOBRAFIAVABAAAYVcAAhAC/gMQCQAM+APICX4GSAbICVgIAABkVxAC/gEQAQAA/AMkASQB/w8kASQB/AMAAGZXgAAQAf4AkAAABP4EkgSSBJIEkgT+BAAEalcAABAC/gMQAQIAugCCAP4PggCyAIoAAABvVwAEEAL+AxACQAlECCQI/AskCEQIhAgAAHdXAAIQAv4DEAEEAPQDFAH0CQQI/AcEAAAAglcAAEACVAr0C1QKVAr8D1QK8gtSCkAAAACDVwACEAL+ARABAAQoBMgFCgQIB+gECAQAAIRXiAiICmgKGApMCkgPeAqsCqwKiApICAAAi1cAAEAEygQ+BAoFfgUKBwAFHgVABX4EAASSV0AIaAlICWwJCgkID2gJTAlICSgJQAgAAJtXAAQQBP4DEAIACKAEngLCD4IBvgKgBKAIolcAAhAC/gEQAQAO/AGUD1QEVARSBNIPEACjVwACEAL+AxAJBAj0C1QKVApUClQK9AsECKZXAAAACP4KqgoqCioPagqqCqoK3gqACoAIq1ckBJQF/gUUBQAF1Ac0BR4FBAV8BIAEQACuVwAEEAL8AxACgACoArgDrAqoCpgKqAaoAMJXAAIQAv4DEAkACfwHrAasBfwIrAisCPwAw1cABBAC/gMQAUAIKAkuBegDLAUsCSAJAADLVwACEAL+ARABAAh8CVQJVAn8D1QJVAl8Cc5XAAAQAv4BAAz4AygAKArICX4EiAduCAgG1FcAAhAC/gEQAQAA6A+oAqgC/g+oAqoK6gffVwAAEAL+ARABQACoAugKCAR+AogFaggMBuBXAAIQAv4BEAEAAvwCtAK2D7QCtAL8AgAC91cAAJQC1AKeD/QCEAhIBv4BCAEIAPgHAAz5VwABEAL+AQABJACkB7QEpgSkBLwErAckAPpXAABIAUgF/AR4BXgHeAV4BfwESAVIAQAA/FcAAhAB/gEQAQAAtAe0Aq4LNgg0CPQPNAAAWAAACAH/AAAG/wEFDnUERQT1B0UEdwcAAAJYAAA4CAoK7AqoCq4PqAqoCuwKCgo4CAAABVgABH4FVgV2BVYFXgcABUoFMgU6BUYEQAAGWBACEAH+AYAAMAD8D6oEqAT+B6gEqAQAABVYAAj+CgoKdgoACnwPPgo8CrwK/AoECAAAIVgQABAEfgUABV4FOgcaBXoFOgVeBVAEAAAkWBAEEAT+AxAKgAi+BqoEqg+qCqoKvgqACCpYAAAQBP4DEAMED/4JVAtUC1QJ/g0ECQAAL1gAAJgJnAl8BVwDDgFcAVwHfAlcCVgFAAAwWAACEAH+ARAA/A8EBPwGfAd8BfwFfARAADFYAABUAXQB3gd0AQAA/g9iCLIEMgPuDAAENFgAAggC/gEIAEAJ/gVqA2oFagNqCX4HAAA1WAACEAH+ARAB1ABUAN4HdAVQBVgF1gcQADpYAAEIAf4AgAm+BKoE6gM+ACoA6g++AQABQVgACEAJfgtqCyoKPg8qCqoKagt+C0AJAABKWAAEEAL+AxABAAF8CFYH1QD8B1QIVAt8C0xYCAEIAf8BCAFAAN8BVQXVB5UBVQnfDwAAUVgAAGgIzAp4CkwKKA+ACnwKVApUCvwIAABUWAACEAL+ARABRAAkByYFVAVUBSYFJAdEAFdY2ggCCooKqAosCqwOqgr6CiwKrAqoCAgAWFgAAggB/wEIAQAG/AEcBl0F+gVcBXwHFABaWAACEAL+AZAFBgWqAnoKigk6BsoAJgcAAF5YAACECrQKtAn0CrQKtg70CrQJtAqECgAAaVgAARAB/gEQBTgE9gfUB9QE1Af0BIQHAARrWAAEEAL+AxABAAj8C7wKvgK8CvwLBAoAAHVYAAAADP4H6gT+BWoFawd+BeoF6gX6BAAAflhECHwJfAv+C3wKAA+ICmgKHgr4CAAJwAiDWAACCAL/AYgICAj6BV4DWwFeD/oJCAQAAIVYAAh+C2oL/gtqC34PEAqSCvoKFgpyCAAAilgAAKwIrAruC7wKiA/oCjwKSAr4CAAJwAiTWAABRAX8BH4FfAV8BXwHfAV+BPwERAUAAZdYAAIQAv4BAAF8ANcP1Ar8CtQK1wr8DwAAmVgAABAD/gGQAAQA7AfkB/4G5AZsBeQHAACcWAAAfgSKBnYGAAZWBzwFtAX0BRYFZASAAJ5YAAAQA/8BgAA8AOYPdAV8BWQF5w88AAAAqFgABCAEfgU6BXoFPgd6BT4FOgV+BSAEAACpWBAE/gMQA3AC2grcD/gDAAr+BIgDeAwAAK5YAAD+CAIKfgoACvYPOwp+Cn4K3gp+CAgIs1gAAhAC/gEQAQQI1AfcB94D1APUB9QLEAjBWEAAfgRqBWoFagVuBzQFPAV2BTQFPAUABMdYAAAQAf4BAAF8BMwH/Af+B/wHzAd8BAQEylgAABAC/gEAA/QK1A70Cd4F9ALUBvQIAAjTWAAM/gPKAf4G/ga+B8IHEgf6BvIGtAQAAdVYAAAQAv4BEABEBbwGvAO+C7wHvAHkBgAE2FgABHAFfgV+BX4FfgcuBX4FfgV+BXAEAADeWAAAEAL+AQAAXAKsDjwJbgk8AuwG/AgAAOJYAAAQBPwDAAj8BwQA9A5kCAQO9AqkCgAA5FgAABAC/gEABXwE7A3+C8YB/AfsBfwJBAHrWCAAIAQgBCAEIAT+ByAEIAQgBCAEIAAAAO5YAAIYAgAB/g8AACAIIAj+DyAIIAggCAAA71iACJ4HkAD+DwAAIAggCCAI/g8gCCAIIAjwWAAABAzUA1QBVAHeAVQBVAFUAdQDBAAAAPJYAAjECFQIVAZUAV4AVABUB1QIVAjEBAAA81gACMQIVAhUBNQD3gDUANQHVAhUCMQIAAT2WAAAxAhUClQIVA9UCF4IVA9UCFQKxAgAAPlYAAA0BLwFfAV8BX4FfAV8B/wF3AUUBAAA+lgAAGQINAu0CvQOPgj0DrQKtAo0C2QIAAD9WJQA3Ab8BvwG/Ab+APwE/AL8CtwP1AKAAARZgAhgCN4ECAPoAhgEAAj+CyAIQAiACAAAB1lAAFAASA+sCrYKpA+0CqwKzArED0AAQAAJWQQKRAo0CYQFpAaeBMQG/AaECZQIJAgAAA1ZAAAICAgKdgneB1wFXAVcBVwLfAkECAAID1kACAQKfAl8BfwHfAV8BXwHfAl8CQQIAAgVWQAAQAAgCBAISAROBIoCCAGIAGgAGAAAABZZQABgCFAEjgKIAWgAEAAgAP4PgAAAAQACGlkAAFAISAlECTQFpgSUBkwCRAHAAEAAAAAcWYQARAD0DwQARAhmBLQDFAOUBHQEFAgAAB9ZIADwAywJ6AkICPgHAACoCJYEVAJMAcAAIFkICagEdgVUAswBEADMAyoB6AkICPgHAAAiWcQAfApsCm4L/AtsBWwFdgNsAXwAxAAAACVZAAj8BNQC/A/UAvwAEAhICdYGtASMA4AAJ1kQCBAEEAIQAdAAPgBQAJABEAIQBBAIAAApWUAIRAhEBEQCRAP8AEQBRAJEBEQIQAgAACpZEAgQBBAEEAPQAj4EUAiQARACEAQQCAAAK1lACEgESARIAkgB/gBIAUgCSARIBEAIAAAtWQAAQAhECEQERAREA/wARANEBEIEQghACC5ZAACACPgIiASIA/4AiAGIBogE+AiACAAAL1mAAEgIyAioBJgDzgCICIgImAgoB0gAiAAxWWAIcAROBEgCyAF+AMgBSAJIBEgEQAgAADRZQARICFIEVAREAsABfgBAAkACQARACAAAN1kAAAQI7ASsBKwC/gGsA6wErAq8CoQJAAA4WQAAKACoAKgCuAOsAqgKqAq4CqgGqAAoADlZAACACIgIqAiIBIgD/gCIA4gEqASICIAIOlkAAKgAqACoAJgGjACKCJgImA+oAKgAqAA+WQgJiAhoBAgECAP8AAgDiARoCIgICAkAAERZSAAoAOgHuAKsAuoHqAqoCrgK6AtICAAAR1kAADQAtAe0ArQCrAKuAywINAj0BzQAIABIWSgEqAyoApgIrAiqB6gAmAKoAqgMKAAAAElZAACgAKwCbAIsArwCrg+sAiwCbAKsAqAAS1lAAEQA5A9UBUwFxgdMBVQFZAXkD0QAQABOWRQEVARMBVwFVgV1B1QFXAVMBVQFFAQAAE9ZkABUCFwFfAVcA94BXANcBXwFXAlcCJAAUVkAAFQJVAl+CVQFQANCAyIFHglCCT4JAABUWSABJAkUBawDJAEmAXUBJAGMDxQBJAEgAVVZJAlUCUQJJAUcBQYDBgF8BQQFFAUkCQAIVlkgCSQJEAl+CQAFSANEAyYFJAkcCQQJAABXWSAAJAEUBfwHdAV2BXQFdAV8BRQNJAkgAFpZAAmECawFZAW0A6oBYgNSA0IFSgUACQAAYFkAAAQF/AXuBe4F3APcA+4F7gX8BQQFAABiWSgBKgGqAK4P6gq7CqoKugqmCqYPKgAqAGVZAAAACXwJDAVkBXYDJANMBQQFfAkACQAAZ1kACXwJBAVsBWwFLAN+ASwDLAOEBfwFAAloWQAALgkgCX4FAAUkA5IBFgORBf0FFQkQCWpZAABUATQB/AP0B/YB9AX0BfQD/AGUARQBblkAABoAmg/+Cv4K+w/6CvoK/gr6D0oAAABzWQAAEAgQCdAIMAkeBRICEAKQBXAEEAgQCHRZiAj4BA4DyAE4CAAMNATEA8QCPAQACAAAdlkICPgMDgOIA3AIBAf8AAQEPAgkCOAHAAB4WQAA+AgOBYgDeAwAAEQARAD8D0QARABAAHlZAAj4DA4DiAF4BgAA/AcQCP4JCAj4CAAGfVkACOgMHgOIA3gEAABECEQI9A9MAEQAQACCWQAACAj4CA4F6AMQAPwPBAQEBAQE/A8AAINZAAj4CA4FiAN4BAAAxAdECEQIRAj8CAAGhFmACIQIvAqkC6QKpgSkBKQHpAikCIQIAACGWQAAGAOAAP4PAACQCHAJHgYQA/AEEAgQCIdZCAj4BA4DiAF4AAAERAREBEQERAT8DwAAiFkACPgMDgPoAhgEcAFEAUQJZAlcDMADAACSWQAI+AwOA+gDGAzwA0gASgBKAEgA+AAAAJNZCAj4BI4CyAE4AgAIaASoBT4CqAVoBAgIllkABPwEBwPEAjwAAAwiAqIBfgChASECIASZWQAA6AieBsgBOAIACDwEgAT+AgACjAEwAJ5ZEAjwBA4DiAN4AAgI4A9cCEQI/A9ACAAApVkAAEQIVAbEB2QGbAJEAmIF8gRKBEAIAACoWQgA+AkOBYgDcAQAAAgP+ABOCEgIyAcIAK5ZAAj4DA8D+AgADP4DEgDSB5IIkgheCAAGs1kAANAIPgUQA/gEAADUAwoI+A8IANgBCAK5WQAI+AgOBYgDeAAADEgCSAH+D0gBSAJIBLtZFAlcCVwLXAvcBX4FXAVcB1wFfAkUCQAAxlkACPgJDAb4BQAA/AdEBFQFVAxEDPwHQATJWQAA6AgfB8gBOAbAASQAJAD+DyQA5AMAAMpZCAj4CA4H6AEQBngARA/EAP4PRADEBwAAy1kAAOgNHgOIAXgCAACwD64EoASsBDAHQADQWQAA+AkOBugDEAj8DyQJJAkkCfwPAAgAANFZAAD4DA4DiAN4BAAAkA+QBP4EkASQDwAA01kAAOgIHgXIAzgAIAicCJAI/g+QCJAIAAjUWagIrAisC5wKzAb8BIwCmgWqBKoIqAgAANpZAAj4BA4D8AgMCYAE/gMAAP4HQAicCIQG3FmgCKwIrQquC6wGvASsBq4FrQSsCKQIAADlWRAI8AQeA5ADeAQAAagHfgkoCTgJKAUkAOhZAAj4DI4DcAQEAOwMrAL+AawCrAS8CQAI61kAAPQMjwLkARgA/g+SBJIEngeSBPIEAATsWQAI+AQPA+gBEAD+BwIE8gSeB5IE8gQCBPtZAAD4DA8D6AEQAv4PAgXiBLoFAgT+DwAA/1kAAKIIlArUC8AGpgSUBIwGlAWkCKwIAAABWgAO+AEIBNgFeALYAwgI/gQIA8oEKggABgNaAAj4CA4H+AQAACQJJAm+DyQJJAkgCQAABFrICMgIrgqYC9gG/gSYBpwFqgTqCMgIAAAHWgAI+AQOA8gBOAZQADQPHAAUADIPUgCAABhaAAj4BA4D+AYAAPwPVARWBNQBVAO8BIAEG1oAAPANHgP4AAQIcAlcB+QBZAN8BcAJAAkcWgAA+AyOA3gIAgb+AZII/gf8DwIA/gMAAB9aCAj4CA4F6AMYAMAPvAK0ArQKtAr8DwAAJVoACPgMDgP4AgAKlAj8DxIE/gKQBVYIAAYpWgAI+AgOBcgDOAj4CJQElgP0D5wI8AgABDFaCAj4CAwFiANwCAwJVAfUAVQDVAVcCQAJNFoAAPgMDgP4APIPBACgAfoHogACCP4HAAA2WoAIwgj+CtoL/gaiBsgGtAWUBKwIpAgAAEZaSAhqCQoLYAscBUwFXAUuBSwHXAlECQAASVoAAPgMDgP4AAAITAw0A0YA9AcUCPQJAARaWgAA6ASeAsgBMAC+B2oFagVOBZoHKQAoAGZaAADoCJwGyAEwBoQDvAD8D7wAvASgAwAAdFpAAF4JQgkyC44F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TAVEBQwJAAC/W4AATADkBxQABADWB1QFdAVUBVQF1AcAAMJbAABMA0QI9A9UAEYLFAh0BJQD1AQ0CAAIxFsAAIwAxAfUBbQFtgW0B9QA1AjED4wAAADFWwAIDAjkC7QGtAb2A7QCtAa0BuQLDAgAAMZbAAAMAaQHhATkBE4HpASUBEQEFA9kAAAAx1tACFQE1ANUANQHVggECHQLVAvUCFQIAATMWwAADACEB/wF3AXeB9wF3AX8BYQHDAAAANJbAACMBLwCvAH8BL4IvAr8CLwJvAKMBAAA01sAAIQP9AD0AvQC9gP0AvQC9Ab0BIQHAADbWwAIDAjEC9QL9AfWA9QD9AfUC8QLDAgAAN1bAAAsAQQB/APECX4IfAt8BXwFfAvMCQAI3lsAAAwK5Ar0CvQG9gP0AvQG9ArkCgwKAADfWwAATAlkBbQB9AmGD5QBpAPUAzQFnAgAAOFbAACMAIQF/AX8A/wD/gH8BfwH/AGMAoAA4lsACWwHRAH0DwQA9gn0C/QF9Af0CKwJAADlWwABXAlcCbwKnAt+B0QGvAR8BXwBDAEAAOZbAAAUCNQH/Af8A/4D/AP8B/wH3AcUCAAA51sAAAwBxANUA9QLagvsD2QDRAPUAwwDAADoW4AEtAK0CrQL9AK2DrQC9Aq0C7QKhAIAAOlbAAFMAWQP9AtUC3YPVAtUC/QLVA9EAQAB61sAAAQB9AT0AvQA5gLEAfQA9AX0BIQDAADsWwAIDAjEC9QH1AfWA8QD1AfUC8QPDAgAAO5bAACMCEQI1AP0CtYO1AL0AtQLRAiMCAAA9VsAAFQI9AfUAfQK1AcGAPQHdAt0C3QOAAT2WwAEFgjWB94H1gPHA+4D7gP+B+4HLggAAPhbAAAIACgAaACIAQgICAgICP8HCAAIAAgA+VsACCQMRAKEAXwCAALQABAJEAj/BxAAAAD6W6AAqACoAKgGqAS8AKgIqAjoB6gAqACgAPtbAAAAAUQBVANUA1QBVAlUCVQHfAEAAQAB/FsAAAABPAFUA1QFVAFUCVQJVAdcAUABAAH+WwAIKAxIAo4BaAYIBFAA0AEQCP4PEAAAAP9bQAREBVQDVAH0BV4BVAlUCdQHVAFEAQAAAVwAAKQEpAS/B6QEpAQAANAJEAj+BxAAAAACXAAAAgF+AVYFVgF/CVYJVgnWB34BAgEAAARcgAT+BKoC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/g8QAP4GkAVWDAAA6lwAAPgDAAL+AwAC+AfAAKYPkgiwCMYPiADtXPgDAAL+AwAC+AMAAO4PoAK+AqAK7AcAAPBcAAD4B/8HAAT8BwAAJAVXBdoPVgVSBRAE9lwAAAAO/ARcBVwHXgVcBVwFXAFcCUAHAAD7XAAA+AP+A/gDAAAoBYwFygKIAi4FaAQAAANdAADwA/4D8AMAAFQDxAD+B8QAVAFEAgAAB10ACXwFGAFYCVgJXA9YAVgBWAUYBXwJAAAOXfgHAAL+AwAC+AMAALQDrAKuCzQI9AcgABRdQAAsAOgPWAVYBV4F+AdYBVgFWAVcBQAEFl0ADP4DDAisCqwKrgrsDqwKrAqsCq4KCAgXXQAAAAD2DxQAVAZ2BtYHdAZ0BhQI9gcAACldAAAMDOwDqAnoDw4ACAjoB6gCqArsBwAALV0IAWsBWgHKB0oBSwECDPoDSgDGD0cAAABKXQAA8AP8A/ADDADsBgwB/A8MAWwCqgQABEtd/AMAAv4DAALwBf4Dyg+qCq4KqgruDwAATF0gAOwPKAUoBegPDgDIDCgCqAEoBqwIYABQXQAA5g8cANwJXAn+D1wNXA2MCPwHBggABmldEACWD/QA9AD0B/QF9gX0BfQH9AiWDxAAi10AAAAA8Af+A/AJ/ARcDV4DXAtcCUAHAAC6XSAAkwCqAqoEsgcHAP4FrgWuAa4F/wUACM1dIAl8C3gFeAV4Cw4A+ATYA/gD3AbQBvAC3V0ACAAM/AMAAAAAAAD4BwAAAAAAAPwPAADeXQAAcAgABv4BIAAAAP4HAABwAAAA/g8AAOFdAAhECMgHAAT4CAQLcAiMCSAK2AgECwAI4l0AAAAF+wWoA6oD/Q+oAaoDrgX9BQAFAADjXQAAAAn+BagFqgP8D6gBqAOsBfoFAAkAAOVdAAQEBAQEBAQEBPwHBAQEBAQEBAQABAAA5l0AAAgMCAPICHwISghICMgPSAhICEgICAjnXQQCBAL8AwQBAAEEAHwERAhECEQMxAMAAOhdAAAAAP4PkgSSBJIEkgSSBJIE8gUCBAAA6V0EAgQC/AEECQAE/AMEAHQABAD8DwAIAAbrXQAFhAR0BIQEBAT8BwQFhAR0BIQEBAUAAO5dgAioBKoDrgioCfgJqA+uCaoJqAmACAAA8V0AAAAA5AckCCQIJAgkCCQIJAh8CAAHAAHyXQAA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dAbUBQYIVAr0C1QKVAoAALVeAAz8AwQAVAD0B9YC1AfUCtQK9AtUCFQEtl4ADvwBBAQkByQA9A6mAKQO9AAkAiQMAAC3XgAG/gECBKoCqguqCPsHqgKqBvoFIgkAALheAA78AQQA9A/0A/QD9g/0A/QL9AukDwQAwV4ADPwDBAj8B6wGrAL+CwQA9AsECPQHAADDXgAM/AMECEwF/ANOAUQB3AdsCWQJFAUAAMleAAz8AwQIdAV0A/QPdgH0D3QD9AVUCAAAyl4ADPwDBAj0D7QItAb2DAQA/A8MAPwHAADTXgAM/AMEAPQLvAu8D/YDBAD8DwwE/AMAANZeAAT+A4IAlgiOBmYHvwZiBW4FRgKeAIIA314AB/wABAL0A7QDtAe2A/YHBAb8AawE/APgXgAM/AMMAOQPvAKmC+4PRAj0BSQD5AwAAOJeAAz8A0QALAusCn4ERANsBfQHtAkUAAAA414ABv4BAgjqBfoF+wX7AfoB+gPqA+oFIgTzXgAAgA98AAQLfA/+DyQEdA10CXQDdAUAAPZeBAi0BKwDZAQABfQJBAkECfwJJAkiCSAJ916ECDQHrAVECAAKJAokCvwLJAoiCiAKAAD6XgIIcgwuA+IECAWqCaoJ/wuqCaoJvgkICf9eAAAQABAA/g8QBBAEEAQQBP4PEAAQAAAAAF9ACEQIRAb8AUQARABEAEQA/A9EAEQAQAABX5AIkAiQBMwDiwCIAIgAigDqD4wAiACQAAJfAAAACR4FqgMqASoBKgEqAaoPLgEgAQABA18ECSQJJAW0AywBJgEkASwBvA8kAUQBBAEEX0AJVAlUBVQDVAF8AVQBVAFUD1QBVAFAAQpfAAl6CTgFPgN4AXoBEAEsAbYPNAFMAQQBD18ICEgESATIB0gESAQIAn4AiAMKBAoICAYTXwAAAADyAJIAkgCSAJIIkgiSCJ4HgAAAABVfAADEAKQIpAikCLwHAAAAAAAA/A8AAAAAF18AAIgI6AioBvwBqACoAPwPqACoBLgDAAAYXwAB5AikCKQIvAcAAAAH+AQEBIAEAAcACBtf8giSCB4HQAD8ByAIEAj/CRAICAn4CAAGH18AAAAEyASqAqwCqAH4D6gArASqBLgDAAAgXwAA8giSCJ4HAABACP4PQATQBEgBRgZACCVfBAHkCKQIvAigBxADzAgICPgPCADIAAgD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iAAACqIJcgQqA+YIIg7gAQAAbWKIAIgI/weIAEAIAghCDv4JQghCDv4JAAhuYggBiAj+D4gAIAhQBE4DwABCCE4I8AdgAG9imACYCP4PWAAACPgPAAgACP8PIAggCCAIcGKIAIgI/g+IAEAIEATQAz4AkAcWCBAIEAZxYogAiAj+D0gAAAf8AMQEBAU8A5AEcAgAAHNiiACIDP4DiAgADvwBZASkBSQCpAVkCAAIdmKAAIgI/g9IAAAISARIA/4ASANIBEgIAAB5YogAiAj+D4gAAAD+DyAEAAD+ByAIGAgQBnxiAAGIDPwDiAgADPwDBAD0BxQIFAn0CAAEfmKIAIgI/g9IAAAIEAQ+AtADEgXUCBQIEAZ/YgAAEAQQA/QABAqkCvQHrAIMAuQAIAMQBIBiiACICP4PSAAACGgEqAU+AigF6AQoCAAAhGKIAIgI/g9IAAAIeAgABP4EAAKMAVAAAACKYoAAiAj+B0gAAAD8B0QIRAh8CEQIRAj8CJFigACICP4HAAD8BwICAgD8DwQABAT8AwAAkmKIAIgI/w9IAAAAIggqCOoPNgCmAGIAAACTYoAAiAj+D0gAAA78AQQA/A8CAP4AAg8ACJViiACICP4HSAAACN4EQgVCAt4FUAgICAAAlmKIAIgI/g9IAEgAAAEiAUwBAAH+D4AAgACXYgAAiACICP4HSAAADOgDLgAoAOgHCAgABphiiACICP4HSAAADv4BIgAiACIA4g8iACIAmmKAAIgI/g9IAEAIBASkA3wA5AckCCQIIASbYgAAiAj+B0gAAA//APgHAAn4CA4K+AkABJxiiACICP4PSAAADIgDfgioBSgC6AUoCAgInmIICYgI/g+IAAAM/ANEAEQAxAFEAnwMAAigYogAiAj+D0gAAAD8DwQIFAvkCLQJBAoACKFigACICP4PSAAAANgHhAiCCEQICAgwBiAAomKIAIgI/g9IAEAAEADIBycIJQnICBAGEACkYogAiAj+B0gAAAz4A0gASgBKAEgA+AAAAKViiAiICP4PSAAAAP4PIgDyDDIDMgXuBAAIq2IIAYgM/gOICAAO+AHIBX4GSAXIBFgIAACsYoAAiAj+D0gAAACwD6wEogSgBKQEmA8gALFikACQCPwHUAAAALAHrgioCOgJCAn4CAAGtWKIAIgI/g9IAAAA/A8kBCIAPgziByIIIAa5YogAiAj+D4gAQAQIAkgB/g/IAEgDSAQAALxigACICP4HiAAAAPwBpACkAPwPpACkAPwBvWKIAIgI/gcIAOAPkASQBP4HkASQBPAPAADCYgAAkAj8B0AA6AyoAv4BqAD+D6gAuAcAAMRiiACICP4PiAAIAIAIiAiKCPoPiAiICAgIxWKIAIgI/g9IAAAI/AkkCSQJJAkkCfwJAAjGYogAiAj+B4gAAAz8AyIAogDiDyIBIgIAAMdigACICP4HSAAAAP4DIgKqCiIKIgb+AyACyWKAAIgI/g9IAAAEKATIBQoECAfoBAgEAADLYgAAiAj+B0gAAA/+AMgHAAn+CggK+AkABMxiCAGICP4HiAACACwBIAH+DyABLAEiAQAAzWIIAYgI/geIAAAA+A+IBI4EigSIBPgPAADQYogAiAj8B0gAAAi8BKQCpAGkCKQIvAcAANJiiABICP8PSAAAAP4PkgSSBJIEkgTyBAIE02KIAIgI/g9IAAgBgADkD1wERAREBMQPAADUYogAiAj+D0gAAAyIA34IqAUqAqoFaggICNZigACICP4HiAAgAJAAzgdICOgLSAjoCwgI12IAAIgI/g8AADgD5gIwCxAF8AMeCPAHAADYYoAAiAj+D0AAEADIAy4BKAHoCQgI+AcAANligACICP4HSAAAALwHIAT+ByAEIAS8DwAA2mIAARgJ/geYAAAImATUA5IAkADUD5gAoADbYoAAiAj/B0gAAACiD5oEhgSiBKIEngcAANxiAACUDJQC/AGSAAMAKAEqAf4PKgEqAQAB3WKIAIgI/gdIAEABFAFUAfwPVAFUAQQBAADfYoAAiAj+D0gAAAT+AwABvAwAAsABPgEADuBigABICP4HYAD+DIgDcAT8CQQI/AsACoAJ4WKIAIgI/g9IAAAO+AEIBIgHbgQIBQgFCA7iYoAAiAj/D4gAAA6QAX4MEATyB5QIVAgQBONigACQCPwHkAAABOgCuAqsCOgPiACIAggE5WKAAIgI/gdIAAAM/gOSAJIA/g+SAJII/gfmYoAAiAj+B4gAAASSBJYEkASYBJYEEAQAAOdiCAGICP4PiACAABgASAhICMoPSABYAAAA6GKAAIgI/geIAAACHAnQBH4FUALUBVQIEAjpYogAiAj+D0gAAAKkApwC1A+cAqQCJAIAAOxigABICP4PSAAAAJIPkgj+CJIIkgiRDxAA7WKAAIgI/g9IAAAEUATQBxAE/AEQBhQMAADvYoAAiAj/D0gAAAnSCCIK+gsmCNIIEgkAAPFigACICP4PSAAACIgE/gKIAIgA/gKIBIAI82KgAKgCagJsC3gL7gcoAygDbgKoAqAAAAD0YgABiAj+B4gAIAgYCSQJ4g8kCQgJMAgAAPdiAAEQCfwHkAAAACgBqAP8CqgKuAqoBiQA/GIAAQgJ/gfIAAAIiQT6A4gAiAD8D4sAgAD+YogAiAj+B0gAAACYD4QEogSiBKQEiAcwAP9iCAHIAfgB/AX6BfoD+gH8AfgB+AGIAQgBAWOQAJAI/AdQAAAAqAKoArwIqAjoB6gAIAACY4AAiAj+D0gAAAgkCSQJvg8kCSQJIAkAAAdjiABICP4PSAAAAN4HZAVkBWQFYgXiBxgACWMIAQgN/gMICEAIXAjEBXcGRAPEBFwIAAAOYwABEAn8B9AAAACoArgDrAqoCrgKqAagABFjgACICP4PSAAACYgE/gMAAP4HQAicCIQGFmOAAEgI/g9AAAQAVARUC0YJ1AhUCAQGAAAZY4gCSgIsArgKqgrsB6gCiAI+AkoCSAIAABpjAAAUAtQD/gOUC4AHtAOeA4QDPANAAyACH2MAAQgJ/gfIAAAIqAiIBv4BiAOoBIgIAAAgYwAAiAj+B0gAAAikBKYDvAC0D8wIrAiABCFjiAGICP4HSAAABC4FIAU+BSAFKAXmDwAAI2OAAIgI/g9IABAAWAlWCfQHXAFUAfABQAAkYwAAiAj+B0gAAAhEBEwDNgAkAFwPRABEACVjAACICP8HAACWAtICsgKaAtIPkgKWAgACKGOAAIgI/gdIAEAJLAUrA+gDKgMuBSgJAAgqYwAAiAz/AwAIkgb+AZII/gf8DwIA/gMAACtjgACICP4HSAAgCBwJYAn+DyAJHAkgCUAAL2OAAIgM/wOICAAO/gHCD1IE0gVSAVIOAAg6YwAAiAj/D0gAAgw+A+IEAAgiCf4JIgkiCT1jgACICP4HQAAQCPgIlASWAvQBnAeUCPAIQmNIAEgI/g8oACAAqge+BKoEqgS6BKIHIgBFY4AAiAj8B0gA4A+kArQC9A+sAqwK5AcAAEZjgACICP8HAAD+DwIEUgT6BVIEAgT+DwAASWOAAIgI/g9IAAAMvAMkBOQPpAikCLwIAAhMYwAAiAz+AwAIvAakAbwIAAf4AQAI/gcAAE1jiACICP4PSAAAAX4BagFqAeoPagF+AQABTmMIAQgJ/geIAAAA4g+sAqACvAKgCuwPAABPY4gASAj+D0gAAAS+BKoEqgeqBKoEvgQABFBjiACICP4HSAAAAO4PqgKqAqoKqgruDwAAVWMIAYgI/geIAAAA6A+oAqgC/g+oAqwK7AdcY4gASAz+AwAIvASsBawG/gSsBqwFvAgABF5jgACICP4PSAAACJQElgLUAZQIlgw0AwAAX2OAAIgI/gdIAAAI4AkuBCoCqgEqBC4I4AthY4AAiAj+D4gAIASYBSQEogUkBAgH0AQAAGJjiAz+A0AIEAj4BJQClgH0AZwClAT0BIAIY2MAAQgJ/geIAAAG/ASEBpYEhAa8CIAHAABnY4AASAj/B0gAAABqAr4Cqw+qAuoCqgIAAGhjiACICP4HSAAQAEwHUgV6BVIFRAVIB1AAbmNAAEgI/g9IAAAH/gAyB7IE8gSyBL4HAAByYwABEAn8D5AABALUB3AJXA1QDVQO1AhAA3ZjAACICP4HCABACfQJVAn8D1QJ8glQAQAAd2OAAIgM/wNICAIIqgaqBKoE/w+qCvoKIgh6YwABEAn8BxAAQAQoARgJTA9YASgBSA1AAHtjgACICP4PSABABwgAJAcqCCIJZAgoAwgEgGMAAIgI/gcAAPwPFAD0DwAAFA7qAQgGOAiCY8AARAb/AQAM/gMCAIIPggT7BJIEkg8CAINjgABECP8HBABgALUHtQD1D7UAvwTgAwAAiGOAAIgI/gfIAGAIaAikBKoHogS6BmoFAAiJY4gI/gdIAAAC+AKoAqgCrg+sAqwC/AIEAoxjAAAcAs0C/wL8Cv4H/AL8Av8CjQKcAgAAj2MAAQgJ/gcYAIwCqgLoA6gCqAqICPgHAACQY4gAiAj+D0gAAADQD0gFBgVEBFwFxA8AAJJjiACICP4PSABAAAgBKAHWDvwHKAEoAQAAlmMAAIgI/geIAAAA5A8UAMYIdAWkA+QEJAiYY4AAiAj+BwAA/g8KAOoOigjqD4oI7g4AAJljAAEICf4HiAAAAqwKpArqB6ICqgLqA4AAm2OAAJAI/AcAACgFvgcoBQAA/A9AAMAAAACgY4AAiAj+D4gAAAT0ApQIlgeUAJQC9AIEBKFjgACICP4HSAAABJQChAHUD4IBsgKKBAAAomMAAQgJ/gfAAAwEpAKUAcQPlAGkAqwEAAClY4AAiAj+B0gAAAikCqwLpgSkBrQFpAigAKdjAAGICP4PSAAMCKQIlAiGD5QIpAgMCAAAqGNIAEgI/g9AADAA/A+qBKgE/geoBKgEAASpY4AAkAj8B1AAAADoA7gCrALqB6gKuAroC6pjiABICP4PSAAAANQHXgVUBVQFXgXUBwAAsGMAAJQI/AeyADgG5gH8AxAIkgj+B5IAgACyYwgBCAn+BwgAAAG+A+oFqgSqAqoIvgcAALdjAACICP4HSAACDvgBTgcAAP4PAgD+AwAAuGMAARgJ/geYAAAC8ANSA1QD8A9cA1ID8AO6YwAAiAj+B0gAQABYCrwKWgkYBTwEWAKQAMljgACICPwHSAAACXQFHANcD3wDFAU0CRAAzWOAAEgI/g8oAAAIXAl8BdwDXgF8BVwFXAjPY0AARAT/AyQAAAD0D5QElwT0B5QElwT0D9BjAAEICf4HiAAADL4CqgSqD6oKqgq+CoAI0mMAAEQE/wNEAAAA6g+qBAoE/gcJBOkPAADWY4gAiAj+B0gAAATsB2wFbAVsBWwF7A8gAtpjBAGEDP8HxAAABt8DVQlVBVUDVQlfBwAA22OAAIgI/geIAAgIeAk2BZQDPANQBXAJAAjhYwAAkA78AQAM/AM0CLQK9Aq0D7QKvAoACONjAACICP4HQAAcD7AAsA/+ALAPsAC8DwAA6WOAAIgI/g9IAAAA/gdoBUAFfgVoBeQHFADqY0AAiAj+B0AAEgH+D0IAGgwAA/4AAAM4DO1jAAEICf4HiAAAAP4G6gSqBaoIqgi+BwAA7mMgAEgI/gdIAAAC+gK6AroC+g+6AroC+gL0Y4AAiAj+D4gAAgRWAvIJ1gbSBNkHVQhACPpjiACICP4HSADAAIwOpASsBOwHogSyBKoO/WMAAIgM/wNACB4IwAteCEAHVgjUCwQIAAQAZIAAiAj+B0AAEAC4BHYENAV8CbQKsACAAAFkAABICP4HAADyD0IAWAeqAtoCAgv+BwAAAmRAAIgI/g9IAAAIqgqYC94EmAaqBaoIgAAFZAAAiAj+B0AAGgj8BTgEugc4CO4JCggABg1kiACICP4PSAAACOwF7APsAewB7AXsCQAID2SAAFAI/AeQAAAAeAN4A3gBfgl4CXwHeAETZIAAiAz+A0gIAAzVA1YIfAlUD1cJVAkAABRkgACICP4HSAAAAKYJWglSCdYPWg1WD9IPFmQAAAgJ/geIAAACVArECqYKng+MCoQKAAAcZIgI/gdIAAAIvAi0CaAG/gSgBrQJvAgACB5kgACECP8HJACAD1oAWgdbBVoHWgDCDwAAKmTAAEgI/gdIAAAH/AB0BnUF9gV0BXQHFAAsZAAAEA78AYAM+AN+CfgHAADeDUICngUgCC1kAACICP4HSABAAG4HVgVcBWQFTgdEAAQANmRAAEgI/A8gAJAPaAB4B3QFcgV0BWgHEAA6ZAABBA3/B4QADAj/BasDqgH/BasDqg6CAERkAACIDPwDKAiABLwGvAG8BbwGvAakCQAARmSAAIgI/gdAABwFVA1cC9QJXAVUBVwJAABHZIAAiAj+B0gAAAasBKQE6geiBKoEqg4AAEpkAABICP4POALIATgBMAD+D6gE/gesBAAEVGQAAIgI/geAAIgCBAK0Au4OxAIEArQCgABYZIAAiAj+BwgA4A8UAFwH9gVcBxQI9AcAAGdkAABICP4PSABMAOgHaAVuBegHaAVsBQAAaWQADPwDBAC0A7QLtguEB7QDtAN0A3QDVAJ4ZIAASAj+D0gAAAl0CXYH9AF0A3YFdAkECXlkAAFEA3wD/AN+C/wL/A9+AvwCfANEAwABg2QAAvwD/AP+C/wLvAfYA9YDUgN2A1QCgAKHZAAAiAf+APAPXgD8B3AA9Ac6BsUBPAYABJBkkACQCPwHkAAYCPwL2AbeBtgC/g+YAgACkWSAAJAI/AcAAJgD/AvYC9wH2AP8AxgDAACSZIgI/gdIABAA9A++ArQK/g8QCO4GiAN4DJVkAACQCPwHAAD8DVgB/AUAAPgPJADkDyQAnmRICP4HWAAACPQK9Ar0CvYP9Ar0CvQKEAikZEAARAz/BwAA8g+/AtoPEAg4BMcDfAwAAKVkAAAICfwHCACgC7wK5A5ACygF/AeoCQAAqWSAAIQE/wNEAAAE+gFWCVMPVgH6ATIMAACrZAABkAj8B1AAAA38ASoB6A0oAegNKAHoDaxkAACICP4HSAACA9YPHgM2AK4DrQcoCwAKrWSAAIgI/gdYANQAVAe8BbwHlAW6BdIHkACuZIAAhA7/AWAE4Ae/BrUG9Q91BHUD/wVgBLBkAACICP4HoABOCfYFVgVAAU4F9gVWBUAJsmRAAEgI/g9IAAIJeAVuA+gBbgN4BWoJAAi1ZAAAiAj+B4gAAARcBd4FQA9cBU4FXAUAALxkAABIB/4AAA78ASwB3AesA38CpAMVBAADv2QAAIgI/gcIAOgMtAL0BBIA8gy0AuQECAjBZEAASAj+BwAAtAxkAwQA+gdUBfwHVAUABMJkAACICP4HCACcB8wFnAW8B5wFzAWYBwAAxWQAAIgI/gdAAHwEzAf8B/4H/AfMB3wEAATHZAAASAj+ByAAvgL6Ar4Cug++AvoCvgIAAMpkQAT8BPwH/gf8DvgPkAfuBqIG5gaoBAgBy2SAAEgI/gdIAAAAmgf8BdgF3gfYBf4FigfNZAAAkAz8AxAIwAW8BfQDlA/0A7wF4AkAAM5kFAA8Ar4DuAPOC7wLhA+oA7YDlAMsA0QC0mQAAYgI/geIAAgOxAGSB9oF8gO0CcQPAADUZIAAiAj+D0gAGA74Aa4MvA/MD7wPyA8AANpkgABICP4PAAT4AxgA2AX+A3wPbAFsBgAE4GQAAIgI/gcIAGAM1APkAr4CvAK8AuwPBADmZAAACA3+AwgJLAmUAdQJvg+0AfQBHA0AAOxkAACICPwHAABeDdQDVAwAAywE9A88CWQI9GSAAIgO/gEADPwDRAj0C/QL9gP0C9QLAAD6ZAAAiAz+A8AIdgfKDO4HCgBuBqoKrgoAAP5kAAGICP4HCADCCH4K3gdeB14H/gvCCgAIAGVUAMwCXgJMA+AL5gvUB8IDSANeAswCVAAPZQAAmAz+AwAI6AdmAvQPAADeB9QL9AoAABJliAj+B0gADAjeC14IXAZMAV4EXgisCyAAFGUAAIgI/gcAAP4H9gR+A/AH7gP2CP4HAAAYZQAAiAj+B0gE+APsD/wJxgH8B+wF/AkAABxlQAAkBP8DIACLD34B+gJ7BvoBegT7ByAAHWUAAJAI/AdQAMQD/AL8DzwA/AP8A+QHIAIkZQAAAABICP4P/gv0B/4O9A+uBPoHqAQAAC9lCAgoBGgEqAQoAz4CKAOoBGgEKAQICAAANmUAAPwDAAEAAf4PAAAwBG4EiAPoAhgECAg5ZQAA5AEkAiQBfAVgDDAEzgIIAegCGAQICDtlBAEEAfwBBAlkCRAEbgKIA4gCeAQICAAAPmUACAgG+AEqCMgHIAAQDO4CiAN4BAgIAAA/ZQQE9AMEAvwDJAJkCDAE7gIIA+gEGAgACEVlCADID0gEfgTID2AIPATKAogDeAQICAAASGUICKgEigKMAygEQAAwDM4CiAN4BAgIAABMZQAAkg+SBP4EkgSCDyAAFAzqAogDeAQICE9lQAD4AVYB9AVUBfQDYAkeBOUCxAM8BAQIUWUIAmgJCAn+D8gACglgCH4EiQPoAhgMAABWZQAEVALUAf4E1ATUA0AAfgaIAWgCGAYABFdlAAj8BVQFVAH8BWAAOAzMAogDeAQICAAAWGVQAlgJVAjyB0QAZAswCO4ECAPoBBgIAAhZZQACVAJUCd4P9AFYCTYIfASLA8gCOAwICFtlAADQBRgE1AQWA4gIMARuAogBeAIIDAAAXWUAAPYP0AH+D9AB9g8ACD4EyQPoBBgIAABeZQAA7g/gAX4CoAnuD0AIfgSIA+gEGAgAAGJlAAAIAvoDqgKqAv4PAAAwDO4CiAN4BAgIY2UAAOoPrwKqAq8KygcgABoMdQLEAzwEBAhmZQgC+ArYCtwO2AN4CmAI/gQIA/gECAgAAGxlRAD0A1YB1AkWDPQDMAhuBogB6AIYDAAIcGWgCKoKmAf+BJgHigggCD4EyQOIAngECAhyZQAA9A/0A/YCdAn0BwAA4Aw+AygD6AwACHRlQABcCDwIvg68CNwP2Aq2CrQKTAhECAAAdWUAAPoPDgP7Ag4J+gcQAD8MxAN8BgQIAAB3ZQAEvwKrAf8FqwW/A5gIPwTCA3ICDgwCBHhlCAl8C3wL/gV8BXwLYAkwBO4DeAQICAAAg2UAAF0JfgX/B1QDXgFoAOYDVgVcBWQFQASHZQAACAgICBgEaASIAgoBiAJoBBgECAgICIllIgQSDNIDVgFaAUsBSgFWAVYB0gcSABIAi2VkAGQPZAFUAVwP1gFUAVwPZAFkCWQHYACMZQgIaASOA3wCAAjUD/QHEAD+AxAEFg4AAI5lJAgkD6QAHAJUCVYPVAEsBSQApA8kAAAAkGWkCKwIrASsBZ4GwAKAAr4GrAWsCKwIoAiRZQAAJAT8AwAIKAbKATgCAAhECPwPRAgACJdlAAIQAiQBJAFIAQgBAAH+DwABgACAAAAAmWUMAKAD/g+gAKwAAAIkAUgBAAH+DwABAAGcZQAEWANUCPIPVAAEAyAAbAEAAf4PgAAAAJ9lQADIA3wF+AT4BXwFAAQoAIAA/weAAAAApGUAAAAO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rAL8BAQEAAB+Z4gBSAD/B0gAgAQ4BIYHYAQBBI4FMAYAAH9nCAOIAP4PiAAADPwDZAikBSQGpAVkCAAAgWeIA0gA/g9IAAAMhAN8CIQFNAIsBeAIAAiEZ4gBSAD+D0gAAACcAWoBCAnICQgM+AMAAIlniANIAP4PSACIAAAIRAhECPwPRAhECEQIkGeIA0gA/w9IAIgAAAz8AyIAIgDiDyIAIACVZ4gDSAD+D2gAgAg4BIgDfgDoBwgIOAgABpdnAACIAUgA/w8oACgCgAFIAP8PaACIAwABmmeIA0gA/w9IAKAIEARuBIgDyAI4BAgIAACcZwAAgAS+BKoCqgGqAf4PqgGqAr4EgAQAAJ1niAFIAP8PSACICCAEqAQoBT4CKAXoBAgInmeAAUgA/wdIAAAH/gAABIADfgCAAwAEAACgZ4gDSAD+D0gAAAEkARwBBgGEDzwBIAEQAaJniANIAP4PSAAAAPwPBAgUC+QItAkECgAIo2cAAAQBtACUBFQEfglUClQKlAq0AAQBAACqZ4gDSAD/D0AAEADIByQIIwkkCcgIEAYQAKtngAFIAP4PKABAD/wABAb0AQQC/AcADAAAr2eIAUgA/g9IAEAAEA+QBP4EkASQBJAHEAC2ZwAApAiUBI4ChAHcD4ABvAKkAqQEvAQABLdngAFIAP4PSAAADv4BCAzwA/wPBAT8DwAAxGeAAUgA/g8oAAAA9A8UAJQBfACUCRQI9AfPZwgBiAD+D0gAAAD4D4gEjgSKBIgE+A8AANBnAACEBIQEvgKsAewHrACsAb4ChASEBAAA0WcIAYgA/g9IAAAA/g+IBIgEiAT+DwgAAADSZwQJZQkRBRIDBAPEDx8DJAMkBSQJFAkAANNniASqBKoCgAKkAbQPjgGEAvwCwATABLAE1GcACVAJVAU0A3wD3A90AxwDFAU0CRAJAADYZ4gBSAD+DygAKAGAAPQPTAREBEQExA8EANxnCAOIAP4PSAAAAPwPJAkkCSQJJAnkCQQI4GcIA4gA/g9IAAAAWABICEoIzA9IAFgAAADlZwAASAgoCKgPWA1cDUgNWA2oDygISAgAAOxnAAAECPQEtAKUAf4PlAG0ApQE9AQECAAA72eIA0gA/g9IAAAA9AMUAfQJBAj8BwQAAADxZwAAiANIAP8PyAAICQAIigj6D4wIiAgICPNngANIAP4PAAD8CQIF+gIAAPwPBAD8AwAA9GegBL4EoAK/AqQBgA+fAaQCpAKkBKIEkAT7ZwAAJAgUCNQPrAquCqQKrArUDxQIJAgAAP9niAFIAP4PSAAAAOgDKAAqAPwPKAAoAugBA2iIAUgA/w9IAAAP/AAUDvQBUghSCNIHAAAEaAAEUARcAlAB0gDUD9AAUAFYAlQCUAQABAVoAAOIAP4PAAT+A0II/gdAAP4HQgD+D0AAB2iIAUgA/g9IAIgEIAKkCSQI5A8kAKQBIAYIaIgBSAD+DygAQAgQBFAEfgTIAyoFqgigBgtokANQAP8PUAAEBnwBRwjkD0QARAMEBAAAD2iIAUgA/g9IAEAEEgSWBJAEmASWBBAEAAARaAADiAD+D0gAAAbkARwDQADQCRAI/gcQABNoiANIAP4PQAAQCAgJJAniDyQJCAkwCAAAFmiIAUgA/g9oAPoPigV+BAoEfgSKBPoPAAAXaAQJdAlUBVQFfAPUD1QBfANUBVQFdAkECSFoiAFIAP8PSAAICGAEGATYAg4ByAIoBEgIKmiIA0gA/g9oAIAEEAROA8gA/g/IAUgCQAQ3aAgBiAD+D0gAAAFKAUoB+A9IAU4BCAEAADhoiAFIAP8PSACICCAEaAWYBE4CKAOoBIgIOWiIA0gA/w9IAAAA/g9SCNIIUgFSBn4FAAk8aMQBJAD/DwQATADkD2cEWwRaBGYE4gdAAD1oEARUAlQB3g9UAVQJEAT+AhAD0gQUCAAGPmhACGQJFAUEBTwDBg9EATwDBAUUBSQJAAhCaIgDSAD+D0gAAAgkCSQJvg8kCSQJIAkAAENogANIAP4PQAAMCYAE/gMAAP4HQAicCAQFRWiIA0gA/g8IACAO+AGWB1QIXArQCRAIAARGaIgDSAD+DygAQAD8DwQIVAr0C1QKVAoACEhoAACUCZQFtAVUA1YPVAF0A5QFlAUUCQAITGgAAAAJeAVYBVgD3g9cAVwDXAV8BQQJAABQaAgDiAD+DwAA/A8EANQDVALUAwQI/AcAAFFokAmYBV4FfgMWAYYPVgFaA1oFsAWACQAAU2jEASQA/w8kAAAE/QUlBSUFJQUlBf0FAQBcaIgDSAD/D0gAyghCCMQFcQZWAkAF3ARCCGNoiANIAP8PSAACBCwFIAU/BSAFKAXmDwAAZWiIAUgA/g8oAIAIVAQ0AxwAFAAyD1IAgABmaIgBSAD+D0gAAAF8AQIBkA8+AUgBRgEkAGhooASUBJACvgKAAagPpgGWApQCjASEBAAAaWjEASQA/wcUAAAO/AEEBCUE9QckBCQEAAB2aIABSAD+BwgA4g9SAVoB+g9WAVIJ8gcAAH9oiAFIAP4HSAAAAX4BagFqAeoHagF+AQABgWhMCWAJEgVYA0IDMg8OAUIDQgU+BQwJMAmFaBABkAD8D5AAoATYA6wCqALoC6gK6AeAAoZoAAOIAP4PSAAACSgN/gMAAPwPBAD8AwAAk2gIA4gA/g9IAAAAVAFEAcYPdAFEAUQBAACXaIgDSAD+DwAA/AisBqwG/AWsCKwI/AgAAJ1oAAAgAPwPAgD4AwAAqAauAJQPtACsAqQEomiEAUQA/g8kAAQA8QdWAVABXgFQBfYHAACmaAAAEgmKBF8ERgVqAkgCSgHfAEYACgAQAKdoiAFIAP4HKAAgAKoHvgSqBKoEugSiBwAAqGhACCoJGgV+BRkDqg8AAR4DQAVABX8JAAmtaIADSAD+D0gAQApYCR4FygaYBp4JqAgACK9oiAFIAP4PSAAABOoErAL4D6gArgS4AwAAsGgAA4gA/w9IAIAM6AOIAOgP/gQIB+oIIASzaIABSAD+D0gAAAy0AywApw8kALQPIAQAALVoAAA0CAwE3gNMAVQBQAJMAN4HDAgUCCQGtmiIA0gA/w9IAAAP/gAKAqoC6gdKCU4JAATAaAgBiAD+D0AAEAUIBSQEogQMBpAFUAQAAMRoRAlUBdQFzAPMAe4PzAHMA8wDzAVUBVQJyWiEA0QA/w8kAEAAngeqAKoA6w+qAL4EgAPLaIgBSAD+D2gAQAkIBf4FWAFYAf4BCA0AAM1okANQAP4PUAAAAPwPVAlUANQHfAkABQAA0miIAUgA/g9IAAAAbAI8Aq4PrAI8AmwCoADVaIQBRAD/D2QATAZEAVQE1wdUAEQBTAIAANhoAAD0DBQC/g8UAfQIYAQUAv4PFAL0DAAA2miAA0gA/g8ABP4Dkgj+BwAM/gOSCP4HAADfaIgBSAD/D0gAAAT8BKwCrAH+D6wBrAL8BOBoAAgYCQoFfAVYA94PWANYA34FCgUYCQAA42gIA4gA/g9IAIAIFAZUAP4PVABUBXwFEAjuaAAApASUAtQPjAKMBD4EjALUD5QBpAKABPFogANIAP4PSABACjQJ1AbeBJQGtAkUCAAA9WgAAMQBJAD/DyQAAAPfAPUH/wfVAF8DQAL6aIgBSAD+B0AAHAD8B/QHVgVUBWQFDAcAAAVpiANIAP4PCAAgALQHrAKuCywI5AcgAAAADWnIACgA/wcIAOAPBAT8BVwFXgVcBfwFBAQOaYgBSAD+DwAAMAD8D6oEqAT+B6gEqAQAABJpgAFIAP4PCAAgDf4DKAiACRwE5AN0BgwIHGmQA1AA/g8QAPAJOAk0BfIDNAM4BfgJEAgtaQAAkAD/DwAA/g8SAO4BAAD4D68C6A8AADBpgANIAP4PAAD8A1QC/A8AAP4PAgD+AwAAP2mAAVAA/AcAAKgA+A94BXgFfAX4D3gAgABKaRADkAD8D5AAAAj8B9QB1AXUA9QJ/AcAAFNpgAFIAP4DAAz+A0IAqgT6B6oEQgT+AwAMVGmAAUgA/g8AACwJfgkMB6ADHgVCCT4JAABaaQAAVAhMBl4DVATAD1QJTAleCUwI1AgACF5pAAOIAP4PQAAcCFQE3ANUAVwJVAlcB0AAY2nAASQA/w8UAIADfwDlD1UFVwVVBfUPBwBtaQgJagVsBXgDbgHoD2gBfgNsBWoFCAkAAHVpiANIAP8PCADiCaII7gj6C0IIkglyCgAAd2mAAUgA/g9IAAAAvA+oCoAKvgqkCqQPFAB8aYABSAD+DygAiAiqCpgH3gSYBqoFqggAAH1pAACkBKQEgAL8AdYH1AH8AYACrASiBAAAgmkAA4gA/g8AAPwHVAJ8CSAExAN8D0QIRASEaQADiAD/D0AAHgjACV4EQANOBtQLBAgABIZpgAFIAP4PCADwB7gC9A8WANYDBAjoBwgAlGkAA4gA/w8AAPwHVgJ8BwAA/A8EAPwDAACVaYgDSAD+DygADAGUD0QJJglECZQPLAMAAJtpgAFIAP4PKACACOwFvAOuD6wDbAUkBQAAnGmIAUgA/g8IAEAIXAbcAVYJVAlUCVQHAACoacQBJAD/DyAAggBKACYA9w+iAqoCqgIAAK5pAATiBOwErgKiAKAPsgCoAqYCqASiBAAAtGkAA4gA/g/IAAAAvA+iCpAKpA+cCoQKvA/LaQAAyAMoAP8PAAD8D74CvAL8A74K/AcQAs1pyAAoAP8HKAAIBugBfAd7BXwFdAV0BwAA0GmAAUgA/g8IAHwAVAhWB/wA1AdUCXwLAAjYaQgDiAD+D4gAAARWBVQA/A9UAlYFVAUAAP1piANIAP4PSAB6ANoP/wraCv8K2gr6DwAAAmrABOwE2gLAArwA1gfWAPwCiAL2BOAEAAAKaiQIFAl/CRQFUAVKAzYBQQM0BX8JFAkkCRFqiAdIAP8PSAGEASYJDA24Dw4BfgUQCQAAE2qAAUgA/g9oAAAIfAt8C/wFfgV8B3wJfAkZaogDSAD/D0gAHAVsAXwJbA98AWwFfAUABR5qAAOQAP4PAAD8BwQJ9ArUC1QJtAqECwAAH2qAAVAA/g9QAAAC9AL8AvYP9AL0AvQCEAIhaoABSAD+D2gAAAl0BXYD9AF0A3YFdAUECSNqhAFEAP8PRAAADSsDqgg+B2oCKwWiDAAAKWo4A7gA/Ae4ACgAgADsB7gHqAeoB6gHAAAqaogBSAD/D0gACAj6CV8FWgX6AV8FWgX6CTFqiANIAP4PAABeCToLjg0cBUIFegtCCV4JOWoAA4gA/w/IAOAItAq+DPQGAASQAP4PEABEagAAyAD+DwgA+gOqAqoC/g94BI4DeAwACEtqkANQAP4PEAAyD7IA8gf+BfIH0QCwDyAAWGqIAUgA/g8oAIAPWgFaB0oF3gZaAMoPAABZasABKAD+ByAA2gS6BboEtQS+B9oFEAQAAF9qiANIAP8PQADmDLgDtArfCKQHuAq0CKAEYWqAAUgA/g9IAAgEuAVsA+4KvAmsB6wBOAJraogBSAD/D0gAEgjyBX8FegX6AX8FcgXyCXFqgANIAP8PAAT8A0QErAdsBwQApAj0ByAAgGoAA4gA/w/IAHgIzAv8C/4L/AvMC/wLAACQaogBSAD/DygAiA98ACcH7gX2BeQF9AcEAJRqiANIAP8PQAAaB/wF2AXeB9gF/gWIBwAAomqAAUgA/g8IANgMuAL0BBYAxAyoAugEAAipagAByAH+B2gAeAXMAfwF/gfMAfwFhAUAAKxqAAOQAP4PAAAUBtQG9gN0C3YHVANUBAAAw2qAAUgA/gcAAP4HAgT+Bf4F/gX+Bf4FEgT7aoADSAD+DwgAfgkuC/4NAAVeBS4LXgkAAARrgAFIAP4PAAD+B+4E5gPwD+YD7gz+BwAACmsIA4gA/g+IATAB1A82D4APNg9UD3QJAAAga0AIMAgMBAoCiAF4AIgBCAJIBDgICAgAACFrAAAEBoQBCAhgCB4ECgPoAAgDSAQ4BAAIImsUBCQCxAE8AwQIMAQOA+gACAMoBBgIAAAjawAE/AMUABQA8gcCADAMDgPoAAgBOAYICCdrAAD8DwQFpATUBAQNYAweB/gACAN4DAgIMmuIAMQPogSQBJIE5AewDA4D+AAIAzgEAAg6awAACAn8BVgB/AUAATAMDgPoAAgDOAwIAD1rUAhYC1QI8gdUDQAIMAQOA+gACAM4BAgIPmsABKoCqgivB6oAogIwCA4G6AEIAzgMCAhHawAB/geqBKoFqgi+BwAAHA7KAQgCOAwAAElrJAStAv4H/AOuAf0EOAwHA+QADAMEBAAATGuAALwHrAW8B4QIvAewCA4G+AEIA3gMAABQawAA/gOCBNoFqgTaBQAMEAPuAAgDKAwYAFNrIAHsD6wHuAeoB+AMYAQcA/ABEALwDAAAYWsAAboPuweAB7sHmgcwAA8O5AEEA3wMAABiawAEAATwBwAEAAQABP4HIAQgBCAEIAQAAGNrAAQEBOQHBAQEBAQE/AdEBEQERAREBAAEZGsACPAPAAgACP4HIAQAAP4HIAggCBAIEAZlayAIIAu8CKAIIAggBP4HJAIkAqQBpAAgAGZrAAAQCNQHFATUB5QEEAT+ABAHFAgUCBAGZ2sACPAHAAT+ByACCApoCKgFPgIoBegECAhpa6AAoAV8CGAIIAUgBf4EJAKkASQApAAgAWprJAikCKQOlAiUCLwIhA+UCpQKpAqkCgAIb2sQANAHHATQBZAF3gWeBbQFFAT0B/AHEAByawgM6AMoAGwL6AdoAi4JfASsA7wEqAgoBnNrEA7QAVAE3ALQD9AAXgn0DFQDdAVUCRAEdGsADPwDBAhUCHQPFAiED1QK9Ao0CtQIAAB3awAM/AMECGwI/A5sCAQPbAr8CmwKpAgAAHhrAAh8B1QE1gc8BUAAPAe8APwPvACoBwAAeWsAAIQARAgkBFQEnAIUAZQAdAAUAAQAAAB7awAAxAhkBJwGFAH0AAQA/AdECCQINAgkBnxrwAAiCJ4EEgPyAAAARABEAP4PQgBCAEAAg2sAAMIIPgSSA3IAAAj4CIgG/gGIBvgIgAiJa8AAIgieBJIDcgAQAM4HqQLoCwgI+AcAAIprgABiCB4GkgFyBBAETgPIAP4PSAFIAkAEi2uAAOIIngSSA3IAAAioBP8EaANlBVUJQASWa0AAYgxeApIBcADkDwQE/AVuBWwF/AUEBJhrgABiCF4GkgFyCAIJqAUvBxgFXQsVC6AItGsAAPwPFAXkBBQNQAzeCEIHQgb+CWAIGAi1awAAAAL8D1ICUgIRCUAI3gVCAl4FoAgYCLdrAAz+A1UJVQk9B0AA4AheB0IG/gVgCAAAumsAAFIGVAHMD1MBAAjeBEIFQgJ+BeAEGAi8awAAbAxsA+4A7AcEAmAI3gRCA14DwAQACL9rAAf/APUOpQD1AqcEAASvAqECLwHoAgAEwGsAAHwJUglAB1QFPA1ACH4JQgb+CWAIAADBawAA/ATSBMAH1AQ8AkAI/gVCAv4FYAgAAMVrAABUATQL1goUB8QCEAjOBUIC3gVQCAAAy2sAACAA4Ak8BSQDpAF8CSQJJAf8ASABAADNawAAIADwAy4CogKqAqoKIgoiDv4DIAIAAM5rAABIAMQHcwJKAsoDegpKCkoO+gNCAkAAz2vAAMgC6AOmApQDlAPUA5QKlAr0B8QCwADSayABIgHqB2oFagX/B2oFag1qDeoHIgUAAdNrEAD4B7YFlAz0BwAAtA8sAKYPJAC0ByAI1GsAAAAA/g8gBCAEAAD+B0AIIAgQCBAHAADVawAAQAF+AUgBKAGADz4BSAFIAUQBJAEAANlrAABACn4L5AdkBVQDXgDkB2QKZAlQCUAE22sAACgBKAEoASQB/AckCaQIkgiSCJAIgAbhayQBJAH8ByQJoggQCMALfgpICkgKyAkIBOtrAADEBPwF7AXsA+4H7AvsCuwKfArECgAA72skASQB/AeSCIIIKApkChAJzgkQCswKAAQPbAAAAAj8ByQEIgQiBH4AogMiBCEIIgggBhFsAAAACPwHpASkBKQE5ASkAaQGvAiACAAGE2wIAPgBCgIIAgAI/g+SCJIE8gGSBp4IAAQUbCAAGABGAFUAVABUAFQAVADUAxQEBAgABBdsAAhYCUYFVAJUAtQF1ARUANQDFAwECAAEG2wQABgJhAUjAyoJKgmqB6oAKgHqAwoMAAYibAgAqAS2Bb4FfAd8BbwFvAUcAPwDBAwABiNsEAAYCUQFIwGqDyoBKgFqDSoA6gcCCAAGJ2wQAMgCxALeAtwP3ALcAtwCHAD8BwQIAAYobAgAUAlYC1YLXAVcB1wJXAkcAPwHBAgABi5sEAiICrYEXANcAlwEXAccCPwBDAYECAAG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AAQRcBAAJA8kAIAFvAT8DrwMvA78ArwEvAmAAh9wEQSmAwAAAgATDLoDOgP4DzoBuwe6A7oPLHAQDKIDhAA2BlQB/gcmAfIEbgVqAfoFAARMcAAAIghEBgQDdA7UDzYPhA92D1QPdA8AAFFwAAAiBqwBAATqA+oH+gXiAeoD+gXqBQAEY3AQACYPQAAXAk0DoAOcA7YL+gtcBxICQABrcAAIYAgYBAACgAF+AIABAAJABDgIAAgAAG1wAAACCEIEMgQCAsIBPgDCAAIDYgISBAAEb3BACHAEAAL+ARACBAAECAQI/AcEAAQAAABwcAQChAF0AA4I5AQkAgQB9AAEAWQCJAwAAHVwAAgACaoIKgQqAqoBKgIqBCoFvggACAAAdnBwBAAO/gEAARgFAAQgBCAE/gcgBCAEIAR4cAAAQAhICigJJAQWAtQBHAIkBKAJIAhACHxweAgABv4BEAEABjAAjgCICAgJCAz4AwAAfXAACAwJ0gQABAgD1gACAwgEDAXSCAAIAAB+cAAAmAiICGgECAIKAewACAMIBMgEGAgAAH9wOAgABv4BAAMIAPAHAAQABP4HAAQABPgPiXBwCAAG/gEAAjgIAgzyA5IAkgCSAPIBAACKcHAAAAz+AwACGAgwBA4C6AEIAygEGAgAAI5wAABACCgJpAQgBBgDxgEYAiAEpAUkCEAIknBAADAO/gEAARgKQAg4BAAE/gIAApgBIACVcDgIAAb+ARACAAjoByoAKgDoBwgICAYAAKtwOAgABv4BAAMIAEgEaAbaBUgEKAcIBAAArHBwCAAE/AMgAgAA/A8kCSQJJAkkCeQJBAitcAAM7AMoCKgJqAQuBqgBKAIoBCgJrAggCK5wcAgABPwDMAYAAPgHLgkoCegLCAr4CQAEs3AYCAAG/gEAAhgA4A8UAJwBfACUCRQI9Ae4cHAIAA7+AQACKAQwAA4A+A8oASgBKAEIALlwAAAADOACoACgBr4ApAKkBKQA5AIEBAAAunAAAYgMigLKALgGrACqAqgIuArgCIAHAAC8cDgIAAb+AQgBEAR0A14I9A9EAEQDRAQAAMFweAgABv4BCAMQADwHpAAkCPQPJACiAyAEwnB4CAAG/gEAARgBAASWBJAEmASWBBAEAADIcCAJEgcOA8oAagwaAAAEfAkAAQAF/gUACM9wAAgADPwBVAFUBVQBVgVUAVQLXAlABwAA2HA4CAAG/gEQAoAIiAT+AogAiAD+AogMAAjZcCAIEA7+AQADWABID64EtASkBLQETA9AANtwQAgwBv4BEAYABPgIiAj+B4gE+AYACAAA33B4CAAO/gEQAP4PAgQiBfoEogUCBP4PAADkcPAIAAz8AwAEEAUAAagD/AqoCrgKqAYkAOZwIAQYBAAD/wAIAwAI+QQJAu8BCQT5BQAI53B4CAAG/gEAApAIhASmA7wAtA/ECKQIgATrcAAIighiChQJAAQqA1oELgSaCIoKeAgAAO1wAAAkBCQDfwAUBoAAZAA/BwQAfAKABkAI+XAAACIIIgY+ADYFNgG3AHYCdgQ+ACICAgQJcQAAEAgUBPQBVAlcA1wBXANcCVwJFAcAAApxPAQADv4BCAMAAF4BVgHWD1YBVgFeAQAAFXEgCBAO/gEQAoAI+ASWAvQBnAL0BIAIAAAZcTgEAAb/AQgDIACsB6QEpgSkBLwEpAcgABpxAAAkChQLvgQMBKQDMAIMBH4FDAUUCCAEIXEoCCgN/gElAfwNJAH8BSQJ/AEkBSAIAAAmcRAIEAz4AV4BWA1YAfwBWg1YAVgBCA0ACDBxeAgABv4BAAI4ANAPSAUGBUQEXAXEDwAANnGICKgGbgJuABQFDAGAAGgGHgBoAooOgAg8cUAIOAQAAv4BEAikBPQDpgCkD/QIpAgABExxeAgABv4BAAKQCPwK1ArWD9QK1Ar8CAAATnEECfwEXQReAVwN/AAEAHYMBQH8AQQMAABVcQAAAAj+BLIAzgK6DAAA/gQSCRIDng8ACFlx8AgADPwDIAj0CpQK/AqUDvwKlAr0CgAAXnEAABgJVgVUAVwN9AEAABwN6gjIAjgFCAlkcTgEAAb+AQABEASEBL4CrAHsB74BhAIABGdxAAj8DZQAlADsDBQAzAFEBVQJVAHMDQAIaXE4CAAG/AEQAgAI/AusCqwCrAasBvwLAAhucQAAMA40APQBdAV+AXQFdAV4AfQBEgwAAH1xAAAQDv4BEA78A6wEbADqB6oEagL6DwAAhHE8CAAG/wEIAgAM/gCqBqsIqgqqAP4GAASKcQQEfAc/ALwAvgb8AAACZwSqAIkCRAwAAI9xAAAICvoK2gLaCv4D2gr6CtkC+QoICgAAlHEYACAM/gMQBCQBlA9ECSYJRAmUDyQBAACZcQAE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t3MEAiQC/AMkAgQAgA+ABP4EiASIBIgPCAC7cwAEJAL8AyQAAAz4A8gJfgZIBsgJGAgAAMJzAAQkBPwDJAIAAPQDFAH0CQQI/AcEAAAAynMAACQE/AMEAkAI/gdCAP4H/A9CAP4PQADNcwQCJAL8AyQBEAFICCQFkgTEBEgCEAEAAOBzBAQkAvwDJAIAAFwGSAH+D0gBSAJABAAA7XMAAEQE/AdEBAAC8AgABP4DAAj8D0QIBAjycwAAJAT8AyACDACMAvQClALUD5QClAIAAv5zJAQkAvwDJAoACPwFVANUAVQPVAn8CQAEA3QAAiQC/AMkAQwDaAIICP4HiAAKA2oEAAQFdAQCJAL8ASQBAAD8D1QEVgTUAVQDvASABAZ0AAQkAvwDJAIACXwIVAnUD/wPVAlUCXwJCXQABCQC/AMkAgAItAcsAKYPJAC0ByQIAAAQdAAEJAL8AyQCAAj0CRQEEALeARAEFAj0CyJ0AAQkAvwDJAKAANQC1Am8CMQHZACUAwAEJnQAAiQC/AEkAQAAtAe0AqwDLgj0DzQAAAAqdAAEJAL8AyQCAAgIBf4FWAFYAf4FCAUACDN0AAAkAvwBIAOIAP4PSAAAA8gA/g9IAIgDNHQAAFoAWgFeAXoBWgFYAXoFXgNaAVoAkAA8dAAIRAT8B0QGAADoBqgArA+oAKgB6AMIBEt0AAAkBPwDJAIAAPQHvAW2BbQPvAXwBQAFW3QAAiQC/AEgAQQE9gSUApAB8ACUApYC9ARcdAAAJAT8AwQI8Ae4AvQPFgDWAwQIyAcQAF50AAQkAvwDJAIAADwAsA+wB/4AsA+wALwPX3QAAJQOVAh8BFQGlApACVQJvARUAVQGAABqdAQCJAL8ASQBAAz+AKoGqgD+BqoBgg8AAHB0AAQkAvwDJAIACHwEVALUAf4HVghUC3wLg3QAAiQE/AMkAgQAdA9kA9YFZANECXQPAACHdAAAJAT8AwAM+gMqCMAHFgcqBOgPKAloCYt0AAAkAvwBJAAAAvQC9AL2D/QC9AL0AhACnnQAAiQC/AMgAQoEaAV+A+gBbgVoBW4JAACndIAAfAh0C7QLtAv8CwAPVAt2D1QLVAkAALB0AAIkAvwBJAUABP4Evge6Ab4CugJ+BRAFynQABCQE/AcgAggM/A/+B+wH7AfoC+gLAAjcdAAIAAb8AQQABAb8BQIEggc+CMIDAAwAAOJ0AAB0DfwB/A90AQAO/AEEBvwFBAL6DwAA43QEAE0N5gMADfwH/AsCCP4HWAHnD1QBAADkdAAE/AX+D/QJ7AHwDfwDBA78BQQG+gsADOZ0AAAEBAQO5AkcCNQEFAUUAPQHBAgECAQG7nQAAFgISA7kB3IHaAdiB2oBcg9kCEgISATvdAAA/AcEBbQE5AQQAQIM/guSAPIPAggABvZ0AACIDPoDiAD+D4oAAAj8D5QA9A8ECAQE93QAAEIIVAzAC2gJZAtWAUwBVA9kCGAEAAAEdQAAfAl8BVwHbAU8AIAPfAiUAPQPBAgABhh1AAAIAAgA/g+IBIgEiASIBP4PCAAIAAAAGnUAAAQBBA/+CVQJVAtUCVQL/gsECQQJAAAcdQAAkg+SBP4EkgcAAP4PiASIBIgE/g8IAB91AAAgCJAIjgiICIgI/g+ICIgIiAiICAAIInUADOQDJAm0CrwKrgqsD7wKtAqkCiQIAAAjdQAM5AMkCLwKpAqkCqYPtAqsCqQKIAggACV1AAArAiQC/wEkAQAIvgbqAb4EqgS+AwAAKHUAAAAM/AMkASQBJAH8DyQBJAkkCfwHAAApdQAAAAz+A5IAkgD+B5IIkgiSCpIK/gkABCt1AAAIAOgPqAKoAqgC/A+oAqwK7AcIAAAAMHUAAPwPRAREBEQE/AdEBEQERAREBPwPAAAxdQAA+A+IBIgEiAT+B4gEiASIBIgE+A8AADJ1AAAAAPwBlACUAJQA/A+UAJQAlAD8AQAAM3UAAPwDJAEkASQB/w8kASQBJAEkAfwDAAA1dQAAAAD8AyQBJAH/ByQJJAkkCfwJAAgABjd1AAAACXwJVAVUA/wBVAlUCVQJfAkABwAAOHVAACAA+AeuAqgC6AOoAugLCAgIDPgDAAA6dQAA/gciAv4DIgL+AwAACAgICPgHCAAIADt1AAD0DwQE9AVUBVQF/AVUBVQF9AUEBPQPRXUAAPgB/g+uD3gAIgnyBDoC5gkiDOADAABMdQAAAAG+CKoEagI+ACoAag+qAL4AAAEAAE91AACACPwP1AjUCPwI1APUBNQG/AiACAAAUXV4CAAG/AEABhAA/A9EBEQE/AdEBEQE/A9UdQAA/AckAvwDJAL8A4AAqAD+D6AArACAAFl1AAA8AKQPogqSCqIKgA+8CoQKRA88AAAAXHUAAEQA1A/UCvwK1g/UCsQKpAqkDyQAAABidQAAoACuAu4CrgLuB64CrgLuAq4CgAAAAGV1AAD8B/wDJAL8A5AAiA/WCKQI3AjED0AAZnUAAPwH/AMkAvwDAAgoCSgJvg8oCSgJAAhqdZAAlADcB7QFlAW8B5QFsgXaBdoHkABAAGt1SAB8BPwH/Af8B/4H/Af8B/wH/AdIBAAAcHUAAAAKvArsB6wGvAKsAqwG7Ae8CgAKAAB0dQAA/AckAvwHdAasA/QDXg1UDVQJVAcAAXZ1AAA4AAoP7AuoC64LqA+oC+wLCg84AAAAeHX8ByQC/AMkAvwDIAC0A7QCrgs0CPQHIAB/dUAA5gdZBdQHWAX/D+AEdgN5B7QIUghABoZ1AAB0BVQPXAzABwQA/A/0DvwP9A78D0QIinUAAPAJ/gj+D/4P/g+uD/4P/g/+CPAJAACOdQAE5AcEBPQDTAIECPQElAL+D5QC9AwAAI91AATEA/QDTAIACKQHPACmDywAPA9kCEQEkXUACW4JVAXUA1IBCA2iAyoE6g82CWIJIAiXdTgJgAT8AwQAJAAkCCcIpA9kAGQAJAAAAJl1MAkABfgDCABIBCgKrAmoCagIqAgoBAAAmnUYCYAM/AMECMQINAQmAqQBZAIEBAQIAACfdZgIgAb8AQQAhAD0B5UElgSUBJQElASEAKR1mASAAvwBBAD0A5QE9gSUBJQE9AQEAgAAq3WICLAO/AEECEQItAWWBpQG9AXECEQIAACudYwIQAb+AWIAIgDyBysIKwkqCfIIEgYiAK91OAGADPwDBAz0AxQAlgLUAxQA9AcECAQEsnWYAIAO/AEEDvQBVATWBXQCVAWUBDQIAAC5dbgIgAb8AUQAJAqUClYJFAWkBCQCRAEAALx1GAmADPwDBAAkBbQE1gjUCrQKlAAEAQAAvnWYCKAO/AEECIQEtASmAuQBpAKkBKQIAATFdZgIgAb8AQQA1A9UAFYD9ABUA1QI1A8AAMd1mASABvwBBATUBxQEFgT0B5QElASUBAAAynWYBIAG/AEEBCQFFAVWBdQHVAUUBSQFJATSdYgIkAb8AQQApAK0AqYC5A+kArQCpAIEANV1mAiABvwBBAD8D6wErgSsAawGfAUECQAA2HU4CYAE/AMECNQJVAtWCVQJVA3UCRQIAADbdZgIgA78AQQA7A+8Ar4C7A+8AqwK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JALkA3wA1AF+AVQB1Ad8AQABl3iEAPQHLATgB8QANAykA0YA9AcUCPQJAASYeEIA+gcWAvIBAAT8DKQC/wCkAP8CpAT8AJ94hAD0D0wEwAcIAHwNSAP8D2gDfAVICQAAp3gAAMoE6gL+D+oKgAr8CtQK1grUDvwAAACpeAAAhAD8ByQC5AsACPwHrAKsAqwG/A8ACLB4hAD8DyQE4AeIAA4J/A8ICPwPCgjICQAAsXgEAdQPLATEC/AHCACoDwgHfgKIBWoIAAazeIQA/AckAuQDAAisB2gALg2oAygErAkgALR4hAD0DywE5AcAAKgL2ArcCtgKqAsoCAAAuniEAPQPTATEB0wA5A9cCVYJ9A9UCVQJTAm8eAAAggD+ByICwAd+A6oEqgD+BqoBgg8AAL54wgD6ByYC4gEABP4DigjqB6oB6gKuBAAAwXiEAfwPJATkAwwIaA+OCOgOSAioD84IKA7FeEQA9AdMBMQHAAB0CFwG3AF2CVQJVAcAAMp4AABSAtIHegVuBWoHagLqB2oFegVCB0AA0HhQBVAFPANUDxYLfAsIC2YLUgu+D5gBgAHVeAAARAD0DywE4APID/wPzgjMD+wIyA8ACNp4hAH0DywEJATEB/wC3AbcDv4K3A/8AgQD6HgADvwBBAS0BLQOtAuWCoQKtAq0CrQOlAD0eEQA9A8sBMQHIACaCdoP1gjWDJoKIAgAAPd4QgD6BxYC8gOACHYHzAFfAMwB9QdUAQAA+ngAAIIA/gcSAuADSgjvBOoC+gDvAuoCAAwBeQAAQgD6BxYC4gEIDP8ArAb9AKwGLAAABw55hAD8ByQCxAsUCEwEXgfQD0wJXglUCRAIGXkAAAQB/APkCw4IVAfUAUAIDAfsDywJZAA6eQAAIAQkAqQBJAgkCOQPJAAkAKQBJAIgBDx5AACIAYoA7g9YAIgBAAD+BwAIAAgACAAGPnkAAAgBiADOD7gACAkgCCAI/g8gCCAIIAhBeQAAiABKAOwPWACIAAAA/A8EAHQCjAEAAEJ5AAGIAMoPeACIACAA/AcgCP4JEAj4CAAGSHkIAYgAzg94AIgJAAT8AyQAIgDiDyIAIABJeQgBiADuD1gAiAgACPAPAAj+DyAIIAgACFB5iACIAO4PWAAIAYAA+A9OBEgESATIDwAAVXmIAIgAzg98AAgI4AwAAvIHxAgwCGwGgABWeQgBiADKD7gAAAj8DyQJJAkkCSQJ/A8ACF15iAFIAO4PWACACDwEJAPkACQA5A88CAAGXnmIAYgA7g9cAIAA+AMoASgB/g8oAfgBAABfeQAAgASWBLQCtAC+B7QAtAK0ApYEgAQAAGB5gABJAOsHWACCAOoDKgEqAeoJAgj+BwAAZXkIAYgAzg94AAgAQAFKAUoB+A9KAUoBCAFoeQQJdAXUBdQB/AnUCdQH/AHUBdQFdA0ECW15EAAQAZgIbgRMAXQJSAlGD1gBZANUA4wMd3kIAYgAyg+4AAAEVALUAX4DVAlUD0QBAAB4eQgBigDqD5gAAADcD1QB9ABUC1QI3A8AAHp5CAGIAOoPWABACAgJ/gVYAVgB/gUICQAAgHkAAAQK/AuMA/wL3AveD/wDjAf8BwQKAACBeQAAFAlMBV4BTAlECVAPTAFeBQwFNAkgAIR5iACIAM4PfAAIAEIEKgIqCeoHKgG+AqAEhXmEAEQA5g9cAAQB8AGqAfgHrAGqAfgBAACNeYAAiADsDxgAwA98AMQH9ALUA1QI/AcAAI95iABIAO4HWACCAPoPagVqBeoHagV6BcIHpnkAACQFswWABesB/gHaB4ABvgOCAz4FAAWneQABigDqD5AABAB8D9wLXgtcC9wLfA8EAap5xAAkAPUPLQBAAv8CrQL/D6oCrQL/AgACrnmIAUoA6g+YAAAI/gv/CuoK/wrqCv4LAAixeQABigDqD5AAxA/8BfwH/gb8AvwP3AIAALl5AACED7wArASsBPwHrASsAqwKugiCBwAAunkAAIAP/ADUAtQC/APUAtQC1Aj8CIAPAAC7eQQABA90AWQFZAfWBVQFVANECXQJBAcEAL15EAAID+gBxAGUBbIHugWyB4QJ6A8IAAAAvnkgBCQCJAGkAGQA/A9iAKIAIgEiAiAEAAC/eQAAkAhUCFQENAN8ADQANAdSCFIIkASAAMB5iAhMCCwEnAOMALwAigqaCSoJSgdIAAAAwXkAApIBUgD+D1IAUgQAB/gEBgTABAAPAAjDeQAAUAhUCFQENAO8AJQANAdSCFQIUAYAAMZ5AAAYA5gA/A+UAAABRABEAPwPRABEAEAAyXkAACgEugS6AroBugH+D7oBugL5AigEAADLeQAAkgNSAP4PUgAACDgEgAN+AIADMAQICM15lANSAP4PUgACAPgBiACIAP4PiACIAPgB0XkAAJQDUgD+D1IAEAFCAUwBAAH+DwABAAHSeQAAlAFSAP4PEgCACDgEgAR+AgAB2AAAANV5EgOSAP4PUgAAAP4PIAQAAP4HIAgQCAgG2HkSA5IA/g9SAIAJYAT6B4IIYAgeCOAGAADfeRQDlAD8D1QAlAkACPwPJAkkCfwPAAgAAOR5AAAUA5QA/A+QAIQAtACEAPwPhAC0AIQA5nkgAawIbASsA7wDrg9sASwDbASsCCABAADneQAAFAOUAPwPkgAACPgIiAb+AYgG+AiACOl5lANUAPwPUgCSCBAETgJIAf4ASANIBEAI63mSA1IA/g9SAJICAAaQAXAA/g9QAJYBEAbveQAAlANUAPwPUgAACHwGRAFEAEQBfA4ACPB5kgNSAP4PUgCSAhACzwEECPwPBADsAQQC+HkUA5QA/A+SABoASA9ICUgJfglICUgPCAD7eQAAlAFUAPwPUgACCagErARWBXQCzAFAAP15FAOUAP4PUwAACZ4JUAhwBV8GUALQAV4AAHoQA5IA/g8SAAAB6gesALwApA+sAKoEqAMFehIDkgD+D0IACAj0BJIDkACSD+QICAgQBAt6AAAYA5gA/A8AATwIpAqkCqQPpAq8CgAIDXqSAVIA/g9SAAAA8gdUAVABXgFQBfYHAAAOegAAlANSAP4PAgA4DMoDSADMD04IeggABhp6AACUAVQA/A9SABAA/g+oBPwHqgSoBAAAIHoYA5gA+A+UAAAM/AMEANQH9ALUCwQI/AcjehAA+AVWBfQFXAX0BiAHpAD8D6QAIgcAAC56AADKASoA/wMACP4Krgr+D64K/QoACAAAMXoUA5IA/g+SAAAC6A+kAqQC6gOiCroK4gcyehQDkgD+D1IAEgDADxIAwg9aBUIF2g8AADN6JAIkAfwPogAABlgAVgVWC1QJXAHwBQAIN3qUA1QA/A9SAAAJfgmWBdYGvgZWBVYIXgA7eooBSgD+D0kAAADKB6oEgQQtBKEE7QcAADx6FAOUAPwPUgBABVwFtAJ2CbQG1ABcA0AEPXoUA5IA/geSABAAdAesB64HtAe2B5YHVAA/eooDSgD+D0kAgA9eAFYPVgVXB14Awg8CAEJ6FAOSAP4PUgCABnwArAysCf4KrAKsBvwIRnoSA5IA/g9SAIAIvAwsBSwF7gYsArwCgABMehAM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fXwsA6AA/g+gAAwBQABECEQI9A9MAEQAQACJfCwDoAD+D6AALAlgBFgGxAFACEYImAcgAJJ8LAYgAf4PoAAsAQAIaAiICw4IiA9oCAgIl3wIACAD/A+gACwBAAj8DyQJJAkkCfwPAAiYfAAALgOgAP4PJACAD4AE/gSIBIgEiA8AAJ58jAMgAP4PhAH8DxQE/AUUBPwEFAX0DwAAn3wECXQF1AXUBXwDVA9UAXwDVAVUBXQFBAmkfAAAgAD8AMQD1gLWAtQK9ArEDvwCgAAAAKV8AADyCJIIngc8A/4PKAAEA3AIkgieBwAAp3wsB6AA/g+oAAAM+AMICEgI7g9ICEgIAACqfAAAKAqoCqoKnAu+AogCmAacB6oKqAooCq58JgegAP4PoAAuAQAA/A9UBNYAVAN8BAAEsXxkCREFQQWMAyABkg8uAUIDwgV+BQwFMAm5fAMA0AH/D1AAkwGAALoAggDjB5oAkgCiAL18AAa4Af4PIAAIAwQIpAanAKQPpACMAoAMvnwCACwH4AC+DwAA1A/cAtwC3grcCtwPFADKfAwAIAP+DywAgA9+BMgDAAz+A5II/gcAANV8BgCgA/4P6AAACKgGqgCsDvgArAyqAIgO1nwGANgA/wNYABAP/AB0A/QC9gL0AvQDIADZfAwAoAH+D6gAJAlABNwHAAisC7wKqAqoC9x8AA78AQQIHAW8BS4Dhg8cA7wDHAUsBQAA33wGAKAB/g+MADoA1g//CtYK/wrWCv4PAADgfAMAkAH/D8IC/wEBAlUB/wfXAz0BkQKBAud8qAH+D6AAKAjgC/wL/Av8D/wL/AvcCwAI73wDANAA/w/eAY0HvQClB+8ApQetAL0HAAD4fIAEhASYA5QB4wDQD0gARAF0AUACgAQAAPt8AAAABJQE1ALcCLQIlA9SAEoBQgKCBIAE/nwAAJAO3ACyDIgCAAD8AwABAAEAAf4PAAAAfQAASAd8ANIPSAEAAOQHJAgkCCQIfAgABgR9SAd+ANAPTAAAAxAATgCICQgICAz4AwAABX0AAEgHdgDQD0wA4AoECAQI/A8ECAQIAAgKfQAAFAhUBVQFfAH0CVYPVAEcBZQJJAokAAt9SAduANAPTADgCggIOATIAg4DyAQ4BAgIDX1IB24A0A9MAAAB+A8IAcgAPgDICPgHAAAQfRAOnADyDsgAgAIECEQM/AtECPwPQAgAABR9SAduANAPTAEAAOgDCAEIAf4HBAnkCQAEGX1IB3YA0A9MAGQBAAT+ByIE/gEiBiEMAAAafUgHWAD2D2AAQAsEB/wAhAQkBTwD4AQgCBt9SAd2ANAPTAAAC3AITgbAAUIITgjQByAAIH0gACoJagVqA+oL/wlqByoBqgWqCSALIAghfUgHfgDQD0wAxAoACAgH+AAuCCgI6AcIACJ9AAA0CBQFVAX0AV4JVA80AbQFFAU0CQAAJ30ACB4JgAXeAcAJogmSD14BUgUeBSIJIAArfRAJHAlQBV4FtAGQD04BFAWUBRIJCAoAAC99AAAACD4FagXqAX4Jag8qAaoFPgUACAAIMH1IB3YA0A9MAcAA/A9EBEQE/AdEBEQE/A85fUgHbgDQD0wAAAMgAKIPngSCBKIEng8AAEJ9SAduANAPTACAAogATgQ1BTQFTAlAAAAAQ30QDtwAsgZIAEAHSABoBloFzAQoBggMAABEfUgHbgDQD0wAAAMACPwPJAkkCSQJ/A8ACEx9SAduANAPTAAAA0IIJgkqCZIPKgkmCUAIUH1IB24A0A9MAKADBACkD6QEvwSkBKQPJABVfbAN7ACiBJgCAAD0BzQJLAnkCTQJLAngBV59SAd4ANYPUAAACygImAQYA84DCAQoCCgAYX1IB24A0A/MAMQDkADID9YEpATcBEQPgABmfUgHbgDQD2wBIACQD4wEogSkBIgEkA8gAG59AAAsCCwFlgHUCcwJgA9eAVIFEgUeCQAAcX1ID3YA0Q9MAAAJBAS8AycALAC8DwQIAARyfQAAkA78AJIGSAIAAFgDdgjQD0wA4AIABHV9SAd2ANAPTAAACxAIiAamBaQEiAawDBAAdn1IB24A0A9MAQAA+AeWCJYI/AicCJQI8AaTfZAMvAHiDNgAAAo6CEIJEgnqD4IJPglGCJl9QAduANAPTAAAAf4PAASoBP4FoAQsBQAEmn0AAEgHfgDQD6wDgAlUBFQDXgBUBtQIBAScfTAO7ACiDJgCAACMDKQCpwCkD6QArAKADKB9MA7sAKICmAQABNoCVAjUD3QATAKABAAArX1IB24A0A9MAEADEAD+D6gE/AeqBKgEAACxfU4HcADsD0AB/A/sA+QDJAKsCyQI/AcAALJ9SAd4ANYPgAL8DwwAvAPkBLQEBAD8DwAAv31IB24A0A9sAQAAvgeqAKoA6w+qAL4EgAPKfQABPgUuBb4F7gGuB6IBWgMaAyYFQgUAAM99SAd2ANAPTAAAAV4EQAD8D2QJcQhGA4gE0X1IB24A0A9MAAAFIASqAioI6gc+AaAGAADSfUgDdgDQD0wA4AOEAJQHfgVUBVwF1gcQANp9SAduANAPzADABZwCrAksCO4PLAO8BAAE4H1IB3gA1g9IAQgAoAeUAJwA1g+cALQHAADjfSAIvAziAtgCgAQgBH4DtAq0B7wBIAYAAOh9SAduANAPTAAAD/wHrAKsD6wCrA+8AoQP6X1IB24A0A9MAEAJFARcA/QJ1AbSBtwFVAjvfUgHWAD2D2ACAAT0BbwHtgW0D7wF8AUABPR9SAd4ANYPSAAAAvwErAKsAf4PrAH8AgQE+32QDtwAsgTAAZwE1AeUBAQA8AwOA/gEAAgBfkgHXgDwD2wAgAIgBGoDqgpqB74AIAcAAAR+RANfAOgPJgAAAfwHvAK8AvwHvAq8CvwNI35AAnwBfAD8B/wASAIYAXwE1ANUANACgAAmfkgHbgDQD8wAQAL2D5gHFgTwD5YIEAgAACt+EA7eAHEEbAMBCHYG4AksCLsK+wsmCgAILn5AB34A0A9MAUAA7A8EANYHdAVUBdQHAAAxfhAO3gCxDMwCAAD3DwAEzgMBBOgHhgiYCD1+kA7cALIMiAIADPwChAaWCaQLhAD8BgAAPn5IB24A0A9MAAAK/Aj8Bf4F/AH8BfwJEAhBfgAIMAq+CjwL/AM8D4gCXgZkB1wKhAoAAEp+QAdOAPAP7AHAB/QHHgT0A5AG/geQDBYCVH6gAy4A8Ae8ABAA/Ae2AtgPPwPQBBYOAABefpAO3ACyBIgCAAiqCeoHrwEKD+oJqgmABWp+AAd4AJQGUgEIADQH8gX6BdIF9AX0BwQAa34AAH4Kfgd/B/4PfgsYB/YC0gp+CpgKgAhwfkgHbgDQD0wBAATwAt4B+gfqAdYC8AQAAHN+kA7cALICCAk+DOsD6wm+BzQMiwN4DAAAfH4QDtwAsgZIAv4PNAi6DzgJPgisDxALAAiMfpgO9gCRBkgBEgjuB/4H7wP+A+4H+gcACI9+gA7cAKIGWAEADPwD9An+C/4O/Av8C4QJln5QB3wAagIACXYE9QcQBPYPMAT+B9AMFgKgfqAEsAVsBSIFkAQAAPwBAAEAAQAB/g8AAKJ+AAAwCewEogSYBgAABAQEBPwHBAQEBAQEpH4AADAJqAVmBRAFQABEAEQA/A9EAEIAQACmfqAEuAXkBJIEgAIwAI4AiAgICQgM+AMAAKd+IAW4BeYEkAIQDIQDfAiEBTQCrAVgCAAIqn4AACAFuAVmBRAFAADEB0QIRAhECPwIAAarfgAAMAXoBaYEkAIACHQEBAP8CAQM/AMAAKx+IAW4BWYFEAQAAqgAqAD+D6gAqASIAwAAr34gBbgFZwUQBQAE9AEEAQQB/wcECfQNBASxfgAAMAXoBaYEkAIACHgIAAT+BAACmAEgALJ+AACYBdcEsQQAAP4PAgCyAdoBAgj+DwAAs34AADAFzgUiBQAA8A8QA/AAnggQCPAPAAC1fgAAMAXsBaIEkAAADv4BAAwAA/4AAAcACLd+AAAgBbgFZgUAAWAIWATEA0AIRgjYB2AAuH4gBbgFZgUwBQAA/A9ECEQE/AFEBkIIQAa5fgAAMAXsBKICmAoACDgEygIOA+gEGAQICLp+AAAwBawFYgUYAQAICAf4AE4ISAjIBwgAvX4AADAJrAVjBRgBAghCDP4LQghCDv4JAAi/fgAAuAXkBJICgAoQCFAEfgTIAyoFqggABMN+uATkBLICAABUBnwBVgj0D0QARAEEBgAAxH4AACAFuAVmBRABAAj8DyQJJAn8DwAIAADFfgAAIAm4BWYFAAD4AygBKAH+DygB+AEAAMZ+IAWwBW4FIgUAAPwPRAREBPwHRAT8DwAAx34gBbgE5gSQAgAIfAZEAUQARABEAXwOAAjIfgAAMAXoBaYEkAQQAEgCVgQkBVQFTAiAAMp+MAqsCWIJEAUAACwBIAH+DyABLAEiAQAAzX4wBZwE8gKIAiAAog+aBIYEogSiBJ4HAADOfiAF+AWmBJAEBACsAqwClA+0AqwCpAIAAM9+IA24BeYEkAAACKQIpAiUD6wIpAggCAAA0X4AADgL5gmQAQAIKA3+AwAA/A8EAHwChAHSfiAAmATkArICCABABugBCAh+BIgHaggIBtN+MAXsBKICmAIAAKQHpASkBL8EpASkBwAA1X4gBbwE4gSYAgAIpASmA7wAtA/ECKQIAATYfjAF7AWiBIACIAiQDqwJpgSIBpAEoAgAANl+IAW4BOYCgAIgAKAPsA+uBKgEsA8gAEAA234gBLgFZgUSAkAAqAKoAq4ClA+sAqQCIALcfgAAMAXsBKICkAIQAEgPtgSkBLQETA9AAN1+IAWcBOIEmAQAAPgHlgiWCPQInAjwCAAG3n4gCbgFZgUQAUAIOAiYBQoCyAUoCEgIAADffiAFuAVmBZIEEAJACGgHXgBID2gISAhIBuJ+AAAgCbgFZAUAAOAPvAK0ArQKtAr8DwAA434wBawE4gSYAgAIrAScA4wAvACaCSoHAADlfjAF6ASmAqACCAhEBeQGagLCBVoMQAQAAOd+IAC4BeYEsAIAAPgHBASgBP4FYASsBQAA6X4wBZwE8gKYCgAI3AVcBF4DXABcBNwJEADqfgAAmAT2ApICAAJUANQHfgVUBVwF1AcSAO1+IAW4BOYEkAIAALQIFAUeA9QBFAVwBRAI7n4AALAF7ASiBDgAoAe0AqwCrgs0CNQHNADwfiAFnATiBJgCAAD4AqgCqAKuD6wC/AIAAvN+AACwBWwFIgUAAPwDtAL0B7QKtAr8CwAA9H6wBOwEogKYAiAA/A+qBKgE+geoBKgEAAD1fiAAmAXWBLECAAC+B6oA6w+qAKoEvgMAAPd+IAX8BbIEAAz+A5II/gcADP4Dkgj+BwAA+H4AALAF7ASiAgAI/AcEANQH9AIECfwHAAD8fgAAIAW4BWYFAAUYAIgCqgisD6gAqAKABP1+AAAwBegFJAUAASgMqAMoBO4PKAkoCQAA/34AALgFZAUSBYgAQAQqAioJ6gcqAb4CoAQAfwAAOAqkC3MJAAyqCJoGBAmgCZIGnguiCAV/AAC8BeIEmQIIAOIHEgTyB14F8gfyBwAABn8AANgCTgJ5AQAE7wQ/AqABKAMmBOwFBAQOfwAAOAXmBJEAAAL8D1ICAAjeBUIC/gVgCBN/IAW8BOMEmALADFQC8gnWBtEG3QVVCAAIFH8QBawFYgUYBQAAtAecANYPnACUBLQDAAAVfwAAOAXkBLIECAKgCKoLmAbeBJgHmgSoCBZ/AAC4AuQCkgAABvwBlAfWA9QHVAHcBwAAGH8gBLwFYwUZAAAFbAVrBaoKqge+ASAGIAQZf5AE3AKjApgCAgDqD2IFfgV+BWIF6g8gABp/IAWwBG4FIAUAAHgBeAV8AXgJ/A/4AQABHX8AANwFswSIACII4AcECK8K6guuCqIKAAggfwAAOAXkBRIBAAz8A3QIrgr+D6wK/AoACCR/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SANABAAA/A8EAPwDK4EADP4Dkgn+BwgA9AySA5AAkg/8CBAIAAQvgQAM/gOSCP4HAADoD6gCqAL8D6gCrgroBzGBAAz+A5II/gcAAHgESgfMAMwPSgh5CAAGM4EADP4Dkgj+BwAA7g8ABEEFzgQABO4PAAA4gQAM/AMkCfwPIASQBSgEpgQoBxAHoAQAAD6BAAz+A5II/gcAAPwB1AFUAX4HVAFUAXwBSoEADP4Dkgj+BwAAFADeB1QFVAVeBdQHAABLgQAM/gOSCP4PAAD0DwQA5gxUA5QFdAgACEyBAAj+B5IA/g8AAOgDuALsB6gK+AsoCAAEUIHADzwApA+kALQGhAWmBZQF1Aj0CJQPBABUgQAM/gOSCP4HAACkCJQIhg+UCKQILAgAAFWBAAz+A5II/geAAEwItAdGAPQHFAj0CQAEZYEADP4Dkgj+BwAB/gjqCqoKqg+qCr4KgAhmgQAM/wPJAP8HAADvDyAEtwQoBSIE7QcAAG6BAAz+A5II/gcABHwDVABUB/wIVAhUAXwHcIEADP4Dkgj+B5IAqgf+BqoEvgOqBLoIAABzgQAI/geyAP4PgAD8D6AExA/4BwQA/AMAAHiBAAz+AxII/gcAAP4G6gHqBOoD/gjABwAAeYEADP4Dkgj+BwAAfAXuBewC7AX8BAAEAAB6gQAM/gOSCP4HAAC8AqwJLAjuDywDvAQABHuBAAz+A5II/geAAXgOeAD4BQgI/gMIDAoGfoEACP4HkgH+D4AB6gS4Bq4GqApqC6kGgAB/gQAM/gOSCP4PAAjuBwAI/AtUCNQJ/AoACICBAAz+A5II/gcAAFQO1AFUCVYJVAlUBwAAioEADP4Dkgj+BwAAeAV4BXgBfAl4D3wBeAGPgQAAZAA8D/wHvAe+B7wHvA/8DwwPZAAAAJiBAAz+A5II/gckAdQF/AHUD/wB1AX0BQAAmoEADPgDCAD4CPgH/gP8B/wH/A3cDxgAAACbgQAM/gMCCP4HEADKBagFrgeoBcoFGgUAAJyBAAz+A5II/gcAAHQFdgf0AXYDdAV0BQAJnYEADP4Dkgj+B4AAVAVMAj4PTAJMBVQEAACggQAM/gO2CP4HAABUCqIKvgpABVQFAgI+AKiBAAj+B5IA/g9AALQGvgR0BQAATAwiAwABs4EACP4HkgD+DwAAVA9WC/wLVAvWC1QPAAG9gQAM/gOiCP4HEAD4B5YItA+cD7QP0A/AAMCB8AAOAPoPugP6A74DgAP2C9ILPgdYAEAAwoFAADwA/A/cA9wD/AOwA7wL9gu8BzQAEADJgQAM/gOiCP4HAADwB7gF9wBVDKQC6AwAAMqBAAz+A5II/gfgAN4G+gH6B+oH1gHwAgAE04EADP4Dkgj+D+QH/g90B/QFHgD8BJwHWAzjgQAA/geSBJIEkgSSBJ4HkgSSBPIEAgQAAOWBAAD8B6QEpAS8B+QEAAyAA3wAgAEABgAI6IEAAPwPlAScB5QEcAuYBKYHVA/UBPQPBADqgQAAAAD8DyQJJAknCSQJJAkkCfwPAAAAAO2BAAAACXwFXAVcA14BXAFcA1wFfAUACQAE84EECKQItAisCKQIpA+kCKQIlAikCAQIAAD0gQAAvASkBJQHlAS0AiAIfgSIA+gCGAQICPqBhAn8DdwP3A3cDd4P3A/cD9wP/A2ECQAA/IEAAPwPRARCBEIEAAREBEQERAREBPwPAAAAggAA1A9UBUQFRAUUBCQFAgViBXoFyg8AAAWCAAAcBfwF/AX6A/AB/AH8BfwF/AX8AwAABoIAAAAJ/AVSBQAFXgF0AVQFAAXUBTwJAAAHggAAAAn8BVQFAAU+AXQBAAVUBdQFPAkAAAiCAAAACfwFBAX8BSwBXAEkAfwFQAX8CQAJCYIAAMAAfAJaAsAC7gfcAsgCXAL8AsAAAAAKggAAJAD0B/QH9gfwBvAG9gf0B/QHlAcAAAyCAAAKAMoPSgRKBH4ESgRJBEkEyQcIAAAADYIQAFAAUAdYBVQF8gVUBVQFWAVQB1AAEAAOghAAUABID0QJUgl6CVIJVAlYCUgPUAAQABKCEABID0QF8wVEBQgHYABqCPIPLgDiAAAAFIIAAJIP/gSSBNAP1AE0CJwPFAA0AlQAUAMXghAASA9WBXwFWAcQAMAPqAL+D6gC6g8AABiCEABYD1QF8gVEDwwA9A9WBVQFdAUcDwAAHIIACbAIUgVWAtIBFgDSA1IC2Q9VAlACAAAeghgJWAn+BtQC/AHUAPwD1AK8B9QCkAIAAB+CQAhABvwBRABMAU4BVQJECEQI/A9AAEAAKoJADPwDRwBVCfwHAADoDygALgDoDwgEAAAsgkAM/ANOAEUN/AMACN4IQgVCBs4FUAgAADCCAABADPgD1Aj4DwAAfg4CAfoHAgj+CAAGMYJADPwD1gBECPwHEADoBycIJQnoCDAGAAA1gkAM/APWAEUI/AcAAOgHigiMCEgIWAgABjaCQAjAB3wAVgn8BwAA+A+IBI4EiAT4DwAAN4JADPwDVwFFDPwDAAhoBFgHzgRoBCgHCAg5gkAM/ANEAFcJ/AcAALgPhgSABI4EsAcgAEeCQAz4A14BSgj4D7QH7AQACCQK/AsiCgACWIJADPwDzwhFCPwHAAC+CKAH/wSgBrwFgAhmgkAP/gBbB/4BAAT/B9UE9wfcBI8HpASkB2+CAAAACPwHVARUBNYEVAFUAlQFfAWACAAAcIIUBCQCxAF8BgAA/g9SCNIIUgFSBn4FAAlygiAAEAD4B5QIlgiUCPQInAiQCPAJAAQAAHOCAAAoASgB/g8oAQgA+AeWCPQInAjwCAAGeoIAAAQAJAQuCiQJpAikCGQILghECAQGAAB+ggQIBAgkBM4EBAMEAgQDzgQkBAQIBAgAAIKCBAAkACQALgDkDyQAJAAkAi4C5AEEAAAAi4KEAJQAlACWCJQIlAj0B5YAlACUAIQAAACNgoQARAA0ALYAJAEkAiQIJAguCCQM5AMAAJKCBABEAMQHTgREBFQEZAREBE4ERAREBAQAl4IECAQJRApmCdQFVAdEAg4DxABEAAQAAACZgoQIpAikBK4EpAL0AaQBpAKuBKQEpAiECJyCggiSCJIIlwSSApIB8geSCJcIkgiSBIIAnYIECEQERAJeBEQMdApECkQJ3ghECEQIBAilgkQARAgkBK4DJAAUABQAJACuDyQARABEAKaCAAgEBvQBVgFUAVQBVAFWAUwBzAMEAAAArIKEAEQIxAiuBKQDhACECKQILgdEAIQABACtggAABAD0B5YIlAjwCJQIlAiWCJQI9AgEBq6CAgDiDyIALwIiAbIAcACiAC8LIgjiDwIAr4IIBIgDCACcBwgIKAhICIgIHAaIAAgHCACxggAABAGEAOQPDgAEAeQHjghECEQIJAgABrOCJAgkBCQGLgHkALQIpAikCK4IpAckACQAt4IACAQIxA8OCAQIBAjwD4QIjgiECAQIAAC4goQApAikCK4PpAykDKQMpAyuDKQGpAiECLmCBAgEBPQDlgCUAJQAkACUD5YAjACMAAAAvYIECJQE1ASWApQClAmUCZQI9geUAJQAhADHggQAFAFUAVYBVAH0D1QBVAFWBVQFFAMAAM2CRABEACQA5gdUCFQIVApUCuYJJAREAAQAz4IECiQJpAQuAqQBdAAkCCQI7gcEAIQDBADRgoQIRAi0BD4D5AAAAOQHJAguCSQJ5AkABNSCAABEAEQPZgVUBVQFRAVUBVYFZA8EAAAA14IEAAQA9AeWBJQE9AeUBJQElgT0BwQABADbggAAFADUB1YCVAJUAtADFAgWCPQHFAAQAN6CggBCACIHugqvCqIKogqiCy8I4gsCCAAE34KEAEQAJAe2AqQCoAKkCyQILggkDOQDAADlggQCJAMkAa4P5AS0BKQEpASuBKQHJAAAAOaCAAAkAKQPrgSkBPQEpASkBK4EpAckAAAA64IAAAQAhAeOBIQE9ASkBKQErgSkByQAAADxggAABAnkCS4FJAf0ASQDJAUuCeQJBAkAAPmCBAEUAVQBFgEUAfQPFAEUARYBVAEUAQQBAYMAAAQA5AaOBIQE9AeEBIQEjgTkBAQOBAACgwAABAzkAy4IJAgkBHQEpAMuBaQItAgkBgODhACUCCQETgMEAOQHJAgkCC4J5AgECAAEBINECEQG5AFOCEQIhAfkDyQELgQkBOQPAAAFg4QElASUArYKtAnUCNQH1AC2AJQChAGEAAmDJAikBKQErgKkAfQPpACkAa4CpASkBCQIDoOECJQIVAhWCVQJVA9UCXQJdglUCUQIhAgngwAABATkBS4FJAX0ByQFLgUkBeQFBAgAACiDBAAkAiQKDglEBDQEJAPkAS4CpARkCAQIK4NEBFQERAIOASQA5AckBDQEPgQkBCQEJAAsgxQBlADUDz4AlAiUCJQI1A+WCJQIlAgEADKDAAjEDKQKjglEBCQMgADkBo4JhAREBgAINIMEAPQHFATWBVQFVAVUBdQFFgT0BwQAAAA1gwQA9A8UDJYMlArUCZQJlAqWChQI9A8EADaDRAAkDKQCpgKUANQPlACkAK4CpAIkBEQAOIMEABQEFAT2B1QFVAVUBVQF9g8UBBQEBABGg4QIlAT2A5QAlAD2D5QAAAD8BQAI/gcAAEmDAAAEAvwCrgKsAqwPrAKsAq4C/AIEAgAAT4MAAIQB5A8OACQJJAkgCeQPLgkkCSQJAABQgxQBlADUDzYAFAFUCVQJ1A9WAVQBFAEUAFKDBAAUCHQElgKUAJQOlACUAJYOlAgUCAQEVIMAAEQJVAVWAzQJGAcQAFQFVgM0CQQHAABYgwQEZAIEAu4PBABECEQI5A9OCEQIRAgEAFqDggiKBKoEiwKKAfoAigGKAosCqgSKBIIEXoNEAkQBVAnWCFQGdABUANQPTgFMAUQCBABgg4QAlAiUDJYCdABUAFQAdACWD5QAlACEAGGDRAhUBEQDDgBEAlQJ1AR0AtYBVAjEBwAAY4MACLQIlASUBJYC1A+UAZYClASUBLQIAAhkgwQEVARUBdYFVAVUBVQPVAVWBVQFVAQEAGWDAAS0DJQElAKWCRQI1A8WAxQFlAQ0CAAAZ4MAADQElAQWAhQB1ACUABQBFgKUBTQEAARrgwAA9A8UAHYClAkEBPQDVglUCVQJ9AcAAG+DRAl0BcQFTgUEBUQAJAC0ACYJJAjkBwAAd4OAAEQA9A8WANQHVAJUAtQLFgj0BxQAAAB4gwAAdAI0ArYKtAq0CrQOtAO2AjQCdAIAAIaDAAAUAPQPtgK0AvAP/A+0Ar4K/AcUAAAAiYNEBFQCVAH2D9QAVAMEAOQDDggECPQPAACKgwAJdAdEAe4PBABECEQIRAjuD0QIRAhACI6DBAAEAFQIlgYkAoQIZAgEBO4EBAIkAsQAkoMAAAQAdAdWBVQF0AVUBVYFVAV0BwQAAACTg0QBJAGcB14FVAXUB9QHVA1WDdQHFAUAAJiDAACUApQC9gKQApgPkALUArYClAKUAgAAnoMACLQI9Aj2BvQB9ADwAPQH9giUCLQEAACrgwAIBAr0CrYKtAa0A7QCtAa2CvQKBAoAALGDhAiUBLQElgKUAfQPlACUAZYCtASUBIQIsoMACFQExAcGBVQJdAlUCdQLVglUCRQJBAC3gwAARAo0CdQHFgBEDEQD5ABOA0QEVAgEALmDAAB0CBQJVglUCdQPVAlUCVYLFAk0CAAAuoMAADwC7AMuA2wDfAM8AywDLgtsCRwHAAC9g1QAVAlUCTYFlAMUARQBlA8+AVwBVAFEAMGDAABUDPQD9gP0A/QD8AP0A/YL9A9UAAAAxYMAADQAFAD2B7QFtAW0BbQFtgXUBxQAAADHg6QI5AQ0A6YCZAQEAKQPpgT0BKQEpAcAAMqDQgBCCXoFLwMiA6IPIgMiA28JIgjiBwAAzIMEAPQPFAi2CrQJ9Au0CbYKlAoUCPQPBADPg0QAVAwUAxYA1ANQAVAB1AkWCPQPFAAEANyDBAlUCVQFFgM0A5QPFAMOA0wFLAkECQAA4INECFQOlAAGDPQDlAmUCvwElgaUCTQIBADpg4QAlACUDtYKlAqcCpwKlAr2CpQOlACAAO+DBABEBFQF9gVUBfQPVAVUBfYFVAVEBQQA8YOkCKQKtAp2BjQFNAU0BXQFtgu0CSQIBAjygwQCpAKkAq4C5A8EAAQA7g+kAqQCpAIAAgOEAACUApQCVgIQAhgPkAJUApYClAMUAwAABIRAACQBFAVWBVQF1AdUBVQFFgEUCPQHAAAKhAAAFA3UBBYCFAH4D/APFAFWApQEFA0ABAyEBADkB6QCrgLkCQQG9AFUAVYJ9A8AAAAADYRECFQEBAM2AXQBFAEUAfQPFgFUAVQBBAEOhKQJtAl0CzYLtAX0BTQFbgdsBWwJpAmkCR2EBAB0CVQJ1gT0BtQG1AT0AtYB1AB0AAQAJIQAADQIlAlWCVQJ1AdUBVYFVAWUBTQIAAAlhAAANAAUB3YFdAV0BXQFdAV2BRQHNAAAACeEIAC0DrQAtgy0APQPtAC0BrYA9A4kAAAAKIQAAPQPFAD2CQQElAO0ANQAngDUALQAlAAshAIAAg/6AasFqgX4B6oFqgWrBfoJAg8AAD2ERAhUBEQDDgCkAKQPtAlUCVYJtA6EAAAASYQUBRQFfAVeA1QB9A90AX4DdAVUBVQFAABXhAIBIgGqAKsPqgr6CqoKqgq7CqoPIgAAAFuEBAIEAXQH9gT0BvQF9AT2DPQI9AiEBwAAYYSAAMQA5A/eA9QD1Af0A9QPFggUCPQHBABjhAAAJAT0B/YH9Af0B/QH9gf0B+QHJAQAAGuEBACkD6QE9gSkAyAI9AcWAFQJFAn0DwAAbIQAANQK1Ap0BnYDFAJ0AtYCtA+0ApQCAAJxhCQIpAacAF4GNAjUClQKdAoeCPQCBAwEAHWEBAhUCVQJVgV0BVQDxANEA24FRAVkCSQAgoQAANQAVAdWAVQB1A9UAVQBVgVUBtQAAACLhHQCBAHkDw4ApAGUBVQBVgk0CbQPFAEAAZmEAAtcC1wH3gbcA1wLXAdcAV4DTARcCAAEnIQEAFQHVADWD1QAVAFEAlQI1g9UAFQHBASyhCQALAxEAxYA9A+0ArQC/g+0ArQK9AcQALiEkgSSBloAOwEKDWoBGgU7CWoAogQSCQAAvIQEACQM5APWD/AL6AvwC9QL9gvkDyQAIAC/hAQAFACUD/YA9Af0BfQF9Af2AJQIlAcEAMSEkACUALQH1gfUB9QH9Af0B9YH1AbUAJAAyYQAADQCFAFWD5QJVAlUCVQJlglUD1QDAAHLhAQARAjUDtYK1A70CtAO1ArWCtQMRAlEANaEAAAEAPQO9gr0CrwAvAa0CpYK9AoECAAE3YQEAAQIZA8OCWQPBAk8Dx4J1AmUDwQIAADmhEABRAX8BP4G/Ab8BvwG/gv8CvwGRAAAAOyEFAhUBMQHDgjkC9QLtAu0C7YLtAs0CkQA7oQACLQE8gMDBPoJWAtaC/4LXwtaC/oLAAIRhQQAdAzUA9YB9ArUCNQJ9gbUBtQI9AiABBOFAAjEC3wLfgv8D3wHfAf+B3wLfAvECQAIF4UEDPQDFAhWBNQBVA1UAVQF1glUAFQOBAAahQQP9ABUDNYB1A/wAQQEpAEmCPQPJAAAACGFJAmkCJQF3gG0CZQJhA/2AdQFtAwUCQAAI4UECXQHRAHuDwQAVAWQBXQJVgk0DxQBAAErhQQIVARUANYB1An0BfQB9gH0A/QF1AMAACyFBASUB/QHlgQEAFQPdABWD1YAdAd0CIQENYXEDzQA9A92BXQHdAXUDX4ElAf0CDQIAAY6hQQAxAcUAMYD1APUA9QD1gdUBhQE9AMAADyFRABUBMQHBgB0AfQH9Ab2BfQG9A70BwAAPYUUANQPxAfuD8QD1A+ECO4EJAPkBSQIAABDhUACSgJKD+sLSgt4D0oLRgvnC1YPQgEAAkmFJAgkBvQAdgF0DXAB9AF0DXYBdAEUDQAASoUEBnQABAauCEQFVABUA0QGLgiECTQIBANphUQIVAwEAo4AhAX0BfAD9AX2A/QJhAcEAG2FAAAkCHQHdgdwD/AHdAJ2D3QHdAckDwQAcoUEAOQB9AHmB/wB7AEEDP4DTADMD0QAAAB0hUIKYgnSCkcFIg0CCPoPqw+qD6oJ+g8CCH6FAABkACwP7gssC3gPLAvsC64LLA9kAAAAhIUkBEwHRAAWAfQB9AP0Af4F9Af0AfQBAAGHhQABNAGEDwYAtA6wALQOhADeCCwH5AUgCJuFAAD0D1QFVgV0BwQAlALeApQP1AKUAoAApoUEDvQBtAi2A/QLtAO0C/QDtge0C/QHBACphQAA+g86AMMN6gMoCKoKrgq/D6oKqgogCKqFBARUAVwJ3gd0AQQM9ANeAFQA1A9UAEAArIWEAJQIRAUGBfQDtA+0AfQDBgVEBZQIlACvhQACdAPUA9YP9A/UD9AP9A/WD1QPdAEAALCFAgiKDf4F2wHeBf4B3gX+Bd8B/gWKBQAAyYW0BLQC9A+2AZQCBADUD/4K3Ar0CtQPAADNhQAA9AT0B/YF9AfwBQQHXAVeBVQHVAQAAM+FAAl0B0QB9A/2A7QG9AYeD/wEFAfUCBAE0IUUBlQGVAPOCiQHBAD0CVQHXgFUD/QJAATVhYoEqgL6D6sDqgECDvoB+wOqA6oJ+g8AAN2FBANcB1wH/gv8C5gLQAc2BxQHdAqEAkQA5IUEDPQDVAn2DwQBdAX0AH4PZAL0BWQJAADlhQAItAXkBQYF9AGwD7QB1AN+A2wFBAUAAOmFIgRqAwIBNwCyD/IKsgqzD6oK6g+iAAAA+4UCACoMSgMLBeIDvgN2D5cPdgO+BeIFAgUHhiQM5AF8Df4BdA3kBAACVAH2D0wBTAYEAAuGRABkC0QI7gVkAnAI9Av2CvQC9Ab0BwAIEYYCCPoHCgRrBWoPagsKC2oLawtqDyoBCgAthgAAAAD+D/8H/gLgB8AC/gP/BT4A/gcAADiGBAD0D/QGfga0BvQLJA7+AH4F/AN0ARQFToYAAAAM8AMQCFAHfgHUAbQHlAi0CLAEEABPhgAAAAz4AwgIKAk+B1wBXAVMCVwNWAMIAFCGAADADzgAiA+oCr4K3ArcCswK3ApICCgAUYYAAAAP8AAQBlAAfgbUCLQKtAiUArAGAABUhgAM+AMICCgJKAs+DWwFbAtMC1wJWAkIAFWGAAz4AwgLqAaoBj4JXAjcC9wI3AtYCggAWoYAAAAP8AAQClAIfg60CLQOlAi0CpAIUABbhgAAgA9wABALUAp+D1QItA6UCrQKMAgAAF6GAAAADPwDBAiUC38HtgO2A7YH5gcsCgAAX4YgAO4EqgSqAyYG8AEYDDgDXwBaB0oEGAJnhgAM+AMYAd4PvA+MCxgI8gCSCJIIkgcAAGuGAAAABPgEiASIBP4HiASIBIgG+AQACAAAcYYABPQFNAU0BfQHNAUsB+wHBAj8AwAMAAZ5hgAA+ASIBP4D+AIABAQEBAT8BwQEBAQABH2GAAAACNwLVAlUCfQHVAVUBVQF3AUACAAIfoYABPgJjgf+B/gGAAgEAPwPJABEAIQAAACAhiAAFAjKDwgEGALwCIgIiAT+B4gEiAX4B4GGAAj4CYgE/gf4DgAIeASCBQwD4AQcCAAIgoYAAPgE/gOOA/gHAAB0AUQBRAl8DMADAACKhgAA+An+B44H+AYACBgI6gwKB4gDeAQICIyGAAD4BIgE/gf4BgAEKAEoAf4PJAEkAQABk4YAAPgEjgf+B/gGAADyCJIIngcAAP4PAACVhogASgjqCVoJTgnqB0oFSgVaB+oHSgxICJyG+AWIBP4HiAT4BBICIgGiCWII/gciAAAAo4YAAPgJiAT+B/gGAAw8AAIOwAkOCHALIAykhiAEIgTSBVYFWgXSB1YFWgVWB9IFIAgAAKqGAAD4CYgE/gf4BgAMJADIAgAC/g8AAQABr4YAAPgJiAT+B4gE+A4ACPwPIgjiDyIIAADAhgAA+A3IBP4D+AoADMgIygj6D8gIyAgACMaGAAT4Bf4HiAT4BgAI/A8kCSQJ/A8ACAAAx4YAAPgE/geIBPAGCADoBwoJjAiICFgIAAbJhgAI+AX+B4gE+AoAAFgARAJTBkQJyAAQAMuGAAASBMoFSgVSBd4HVgVWBVYH1gUSCAAA1IYAAvgCzgP+A0gCMAP+D2IEkgRiBP4PAADZhgAA+Az+B/4H+AYACCQJJAm/DyQJJAkgCNuGAAj4Bf4HiAT4BgAAXgZIAf4PSAFIAgAE5IYAAPgESAT+A3gDIAC4D6YErASwBKAPAADuhgAEFATUBUQFXAXGB0QFXAVEBdQFFAgAAPmGAAj4Bf4HiAT4CgAA5A9kAvQPbALkDwAA/oYABHgE/gN4AwABlAj8B5AMfgKQBVYIAAYAh0AAXAD0BXQFfAX0B3QFfAX0DTQI/AcAAAKHAAj4Cf4Hjgf4ClAASAVuBdQPbAVEBUAECIcACPgF/geIBIgE8A5MCVQF1ANUBVwJQAkShwAA+AX/B/gGAgi+B0II8AsCCv4LIgoACBOH+AWIBP8HiATwBhoP5gUACCQK/gsiCiIKFYcAAPgEjgf+B4gE+AIwDM4DyA9OCHkIAAYXhwAA+AX8B4gEcAKMD1QAVAH0AVQI3A8AABiHAAR8Av8DxAd4DCsC/AEkA/4FAgL+BwAAHIcAABQEpAekBrQGtge0BqwGjAaUBxQIAAAhhwAA+ARIAv4DeAMABtQHXgVUBV4F1AcQADuHAAD4BYgE/gf4CgAA/A88Az4DPAv8BxAAR4cAAPgJ+ASOB/gGAAj8A7QC9Ae0CvwNAARJhwAA+AmOB/4HiAT4BnAAqgL4D6wC+gIAAEyHAAR4Av4DOAOAAP4PkAAkAAgB/g+AAAAATof4CYgE/geIBHgB4AdeBGoFagVqCP4HAABXhwAE+AWOB/4HiAfwCLwK1ArWD9QK/AqACFmHAAj4Bf4HiAR4AsAHvALUB1YB1AdUAdwHYIcAAPgJmAj+B/gKAAD0D1QK1A9UCvQPBABmhwAI+AT+B4gEcAL+D5ICDghgCWIH3ggACHSH+AmIBP4HiAR4AIAHfwKAD/4DEgj+BwAAdocACPgF/gf4BgAAfA1IA/wPaAN8BUgJAACChwAI+AX+B4gGcAT8B1YFPAL4DwQA/AMAAIOHAAj4Bf4HiATwDgQIVAbcAVYJVAlUBwAAjYcEAPwP7ATsAnwJgAd4AIgI/geIBPgGAAiehwAA+ATOA/4DSAIwAX4GqgL+AaoJggcAAJ+HAAT4Av4DyAJ2A34JWgVaBdoBWgV+BQ4JoocAAGIE6AfuBuQG4AfyBugG7gboB3QIAAi6hwAA+AmOB/4HiATwBR4J6g1+DyoBvgsACsCHAAj4Cf4H+AYACJQC9ALuDtQCBANUAgAAxocABPgCSAL+A0gCMAtkCHYH9AF2A3QNAATLhwAE+AX+B4gE+AQAAlwENAl8CTIAWgaQANGHAAj4Bf4H+AYACPQDdAN0A3YP9AMQAgAA8ocAAKALrAqsB6wHPgisA6wKvAesBqAHAAj5h4QEfAQ3Bb4FtgV4B8oHtgWyBboHNggAAPuHAAT8Av8DRAJ+ATsL2g9+A+oHKwdqCwAJDYgACPgF/gfwBnwPPAO8B+ADMAf+AAgDOAQViAAI+AX+B/gCAgA6D0oH3gFaD2oBag9AAB+IAAD4Cf4HiARwAiYPeAt0APoPUAR+AwAMIYgAANQF3AW+B78Ftwk/AJcFrQesBaQFAAAiiAAAqgWeBf4H/gX+Cf8A/gX+B54FqgcgCDuILAAXBN4FQAV2Bf8HfgVwBWQFvwc0CBIAQIgABPgHCAQIBPwHCgQIBPgHCAQIBPgHAARFiAAA+Af8B/oH+AMCABwBIAH+DyABLAEAAEaIAAggCbwEJAS8AKYPfAEkAiQFvAQgCAAATIiIAEgA5A8SAAAAJAAkACQIJAjkDyQAIABNiIgARADzDwgAIgwiAoQBIAgiCOIPIgAAAFOIQABEAPMPCQDIAf8HCADDBRAE8gcSAAAAVIgAAMwA8g8YAIYI9QeUBAAAJAjkDyQAAABXiEAARADyDxkCUALeA1QCVAYABPIHEgAAAFmIAADIAOQPAgBUD3wFdAdkACQAJAjkByAAW4gEAEIA+Q8IAPoD3wLaB/4CAgDyBxIAAABdiAAAJADyDwgAeglaCf4H+gUJBCAA5A8gAGGIAABEAPIPCAn4Ba4D/AOsDfgBAAjkDyAAYogAAIwA4A8cALwHnAeYB7wHvAQAAOQPIABjiIAAiABICMgPKAgaBDwEyACIA0gEKAgACGWIAAAIAYoA6g+YAEgBAAD+DyAAQADAAIAAaIggATQJtAS0B3QEPgT0BDQBtAK0BCAIAABpiAgBiADuD5wAQAkcCOQEFAOEAnQEDAgAAGuICAGIAO4PnABIAQAIiARIBEQCMgIQAQAAbIgIAYgA7g+cAEgBAADQABAJEAj+BxAAAABwiBQBFAV8BVwH3AReBNwEXAFcA3wFFAUQBHeIAAAEAnQCVA5UCfYIVAlUAlQGdAkECQAAgYgAAhAC9AK0DrQJvgS0AbQCtAZ0BRAJAAiEiAQBhQD3D44AZAEADCICogF+AKEDIQQgBIuIkAKIArwKgg6ICcgIjgmYAqoGygrKCqgIjYgAAYwA7A9YAQAA8AcsCSgJ6AsICvgJAASSiAgBiADqD5gAAAj8CSQJJAkkCSQJ/AkACJaICAGIAOoP2AAAAPgPiASIBP8HiASIBPgPnIgAAYoA6g/YAAAESAJIAf4PSAFIAkgEAACriAgBiADKD7gBAAz4A8gISAl+BkgGyAkYCK2IhAKkAqQKlA6MCaYIvAnWAtYG1AXEBbQIsYgAAYgA6g/wACAA/A8CABAH/gCQAxYMAAC0iAgBiADsD5gAAACoALgDrAKoCpgKqAagAMGIAAFUC9QHXgRUAVQDEAz+AhAD0gQUCAAGwojWAtYC3gquDp4JxgSAAZ4CwAbABf4JAAjFiKACrAKQCrwOgAmoBKgBvAKoBqgEqAgAAMaICAGIAO4PnABIAQAELAUgBT4FIAXsDwAAz4gABEQFfA18D3wJ/gl8A3wFfAV8C0QLAAjUiBQA1A90APQHbAVkBWYFzAbUAKQPJAAAANWICAGIAO4PWACIAAABxA+yCIgIsgjED4gB2IiABKwCrAqsDoQJ3gSEAaYCrgasBKQIAADZiAABiADrD9gAgAIqAeoHvgSqBKoEvgcIANyICAEIAc4PuAAAAOgPqAKoAv4PqAKqCuoH3YiQAr4CmAqYDr4JgASoBKgBvgKoBqgEiAjhiAgBiADsD9gAAAB8CVQJVAn8D1QJfAkAAOSIAAGKAOoP2AAADPwDRAC2ApQPlAKUAgAA84gAApwEhQK2DrQJ9gS0AbQCtgaFBJwIAAD4iAgBigDqD9AAgAT8BNQC1A/8D9QB/AKABPmIgAJEAnwBfAf8BP4EfAF8AnwCfAXEBAAA/YgIAr4CnAq+BpwF/ASAAZwCwAbABb4JAAQCiQAAxAD1D2wAAASUBN8HlAQAAP8HIABAAAeJgABIAO4HeAAABT4F/gW8ArwCvAW8BAQEEIkIAYkAyg+4AAABvgeqBKoFqgaqCL4HAAASiSACFAL0AgQOZAl8CT4JfAI8BnwFJAkABCWJAAGIAMwP+AAADvwBdARcATwJ3A8cAQAAKokAAIgB7A/YACAI7AcACPwLVAhUCXwKAAgyiQABlADUB7AAAAT4A+gD7AP4B+gD6AMIAkSJQAXEBfwD7A/8CcYJ/APsBewF/AlECQAAX4kAAIgA7g/cAQAJTAVeAVAPTAFeBVQJEAByiQAFDAV8A34PfAt8CQAJfgP8BfwL4AtACX+JBAD0D5QE1AQ8BBQEFAR8BJQElAT0DwQAgYkAAIIIugiqC/4GqgSqBL4GqgWqBLoIgACGiUQBXAG8D1wA3AjsC/wH/Af8B/wLPAgACIeJAgBeB9YH1g/eB1YHFgz+A7YCtgr+BwIAi4kACAAI/AlUBVQDVAFUAVQHVAn8CQAIAASPiUgISAT8A0gBSAoACPwFVANUD1QJ/AkABJOJAAgMCPwL5AbkBuwC5ALkBuIK+gsCCAAElokIAYgAzg+4AAgJAAj8BVQDVA9UCfwJAASaiQAIOAgKCOwL6AbqAuwC6A7sCwoIOAgABKeJAAg8CPwL/Af8B/wDwAPsB+gL6AsoCAAEqokAAKQErALmD6wCoAj8BVQDVAFUD/wJAASziQAAMAHsB7gH6AeoB/wNVANUAVQH/AkABLqJAAAwCB4I3gvQB9oD2gPYD94LHggwCAAEvYkACD4I7gv+B+4H7gPAA/YH/Av8CzQI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AAAAAPwProsgACQI7A8ABAQCeAiCBQwCgAV4CAQIAAiviyAAJAjkBwgCQAJEAPwPRAAEAPwHAAgABrCLIAAiCOQPCAQAAMQHRAhECEQIRAj8CAAGsosgACII5AcAAogIiAT+A4gAiAD+D4gAgACziyAAJADoDwAEiAKoAKgA/g+oAKgEiAMAALaLEAASBvQBBAWADDoCIgGiCGII/gciACIAuIsgACIA7A8ABLAAjgCIAPgPiACIAIgAAAC5iyAAIgjkBwgCYAD4DwYAgAD+ByAIGAgIBrqLIAAiCOQPCAIgAJAHCAmGCIgIkAggBiAAvIsgACII5AcIBGAAGA6GBWAEBgUYB2AIAAC9iwAAJAjkBwgCAAz8AyQGxAEkAvwHAAgABL6LIAAiCOQHCAIgCqAIXgVCAkIGfgXgCCAIv4sgACIE7AcEAgAICAb4AUoISghICMgHCADAiyAAIgTkBwgCQAlIBEgD/gDIA3gEQAgAAMGLIAAiCOwHCAIACOQPBAgECPwPRAhECAQIxIsAACII7AcEBIACugCCAP4PggCyAIoAAADFiyAAJAjoDwgEAAD8DyQJJAkkCfwPAAgAAMaLIAAiBOwHCAIACHwGRAFEAEQARAF8DgAIyIsgACII7AcIAmACEAAOAPgPKAEoASgBKAHJiyAAIgjkDwgEAAD8DyQAJAHkDyICIgIAAMqLIAAiCOQHCAQAAlAISAkGBcgEUAIQAQAAzYsgACII5AcABgIA6gMqASoB6gkCCP4HAADPiyAAIgTsBwQCIACiD5oEhgSiBKIEngcAANGLIAAkCOgHAAIkAqQCrAKUD7QCrAIkAgAA1YsAACII4gcCBAgASATIBwgC/gAIBwoMCALXiyAAJAjoBwgCoACoAqgCvAioCOgHqACgANqLAAAiDOwDAA74AygAKALICX4EiAfqCAgG3YsAACQI6AcIAiAApA+kCPwIpAikCKIPIADeiwAAIgjsBwAAsg0uA8IE8AkCCv4LIgoCCuGLAAAiAOwPAAD4DxYA1AdUCFwK0AkQCAAE4osgACQI7AcABBAAyAOuAqgK6AsICPgHAADliyAAIgjsDwAEJAG0CKwEZwQkBhQFhAgAAOaLIAAiCOQHAAIIAEoBSgH4D0gBTgEIAQAA54sgACQI6AcAAhgACAEoAe4HqAiICJgEAADriwAAIgjkDwAA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gAT/BJgEmA8YAL+MAAQQBPwF9APsA/AB9AHsA+QD1AUMBAAAwIwAABQIzAvmCvQK7ALgAv4K8gryCx4IAAjDjBAACADcA8QL4AvqA+oD/gvqC+kDKgAAAMSMAAj8DVQFVAH8BUAA+A+sAqgCqAroBwAAx4wQCBII9Av0BuQG9AL2AuwG9Ab0CxQIAADKjAAI/A1UAVQF/AFIAOgJSAR+AogFbAgIBtOMIAAsCOwH/AbsAv4C7AL8AvwG/AcMCAAI24wAABwIzAXuBewF7AHgAfwF7gX8BRwIIADcjAAI/A1UAVQF/AUAAb4C6gmqBKoDvgiAB96MAAAcCMYP9g30DfYF9AX0DfYNxQ8cCAAA4IwACPwFVAVUAfwFIAC0B6QEpgSkBLQHJADijAAIPgj6B/4H+gf6A+AD4gfaB5oHJgggAOOMAAQ6CO4H7gf+B+8D7gP+B+4H7gc+CAIA5IwACPwFVAVUAfwDqAjsBBgH2ga8CbgJEATmjAAI/A1UAfwFAAjUDxQI0AeQBP4DEAwUBuqMIAAcCMwL7Ab6BvgC7gLqBuoG2gsKCAgA7YwABPwHVAFUA/wFAAGkB/4FpAW8BaYHIAD0jAAEeAJYAfwPWAE4AMAL7ArkAvQG7AcACPqMAAD8DVQB/AIICGwF+A9oAfgPbgPoBUAI/IwACPwNVAFUA/wGwA/sAv4C7Av+CuwHIAL9jAAArAh8BPwH/AP+A/wD/AP8B7QEpAQAAAiNAAj8BVQB/AUAAPwP5wrkCvwK5ArnD3wACo0AACgI/g9eDVwFXAV8BV4FXgXsDxwICAgPjQAMggO2CjoHugm7A7oFOgC6B7oPCgQAAB2NAAAACPwJBAQEBAQC9AEEBAQE/AkACAAAHo0ACAAI8AsQCBAEHgLUARQEFAT0BQQIAAAfjSAIEAj4CRQIFgQUAtQBHAQQBPAJAAgAACGNAAAUCNQLVAhUBlwBVARUBFQI1AsUCAAAIo0ACP4JAgT6AwIA/g4AAAgDiAhoCP8HCAAjjRAAFAjcC1wIXARcA14AXARcBNwFFAgAACSNAAAeCMAFQAReAkABZgRaBFoI1gsSACAAJY0ACP4IAgb6Af4MAABwCI4EiAN4BAgIAAAmjQAEfwf5AAEC/wIACP8HIAToACYDIQQAACeNAAAICMQLXghBBkQBTwRUBFQI0gsKCAAAKI0AAPAHDADsCSwELAK8ASoEKgTqBQgIAAApjQAAfA/0AAQC/AAADvwBNAzUAhQD8gQECCqNCAAICNQLVAhSBFoDUgB0BFQE1AUECAgAK40IACgI7AtqCFoESANoAGoEXATEBQgICAAsjQAI/g3yAwIC/gAADCQCIgYqCeIIIQgACC2NAAj8DeQDBAD8DhAAzAEKAcgJCAj4BwAALo0AAPwJBAT0AwQA/A4QAAgEDgQIBAgEOAQvjQAACAj8C2oIagZ+AWoEagRqCP4LCAgAADCNAAAoCLgJuAS4ArgIuAsICP4BCAYOCAgEMY0ACPwIBAb0AfwCAAhQBP4EUAdUCVAJAAQ0jQAI/A30AwQA/AYAAIAP/gSIBIgEiA8AADWNAAAgCPwLdAh0BHQDfgB0BHQE/AUgCAAAN40QAAgI3AtCCEgESANOAFgEagjiCxQAAAA4jQAAAAjcC1IIWwRAA1IATgRiBOIFHggAADmNAAhACN4LdghfBFYDVgBfBFYE1gUWCAAAOo0AABQI1AtOCGQGXAFABFwEVAjUCxwAAAA8jQAE/AL0AQQE/AAABugBCAh+BIgH6ggIBj6NAgg6COoFagR+BGoDagB+AmoE6gU6CAIAP40AAPwN9AMEAPwFwAD4D64CqAKoCugPCABBjQgABAjfBUAEagNqAGoCfgJqBOkFKggAAEKNAAj8BuQBBAT8BAAAyA/WBKQE1ARMD0AAQ40AAPwN5AP8DAAM+AMICEoI6g9ICEgIAABEjRAIEgjUBUAEaAJmA1YATAJUBNQFDAgAAEqNAAD8DeQDBAD8DgAAUANoCOYPaABIBxAES40AAPwN5AP8AAAI1A8UCNQHEAD+AxAMFgZMjQAA/A/0AQQE/AWAANQPfgVUBVgF1A8QAE6NAAj+BfIDAgD+BQAApAlUB/4BFAU0CQAAT40AABwIhgu0CLQEtgK0ALQEtgSFBRwIAABQjQAI/AkEBPQDBAD8DgAAvgDqC6oGqgm+B1SNAAj8B+QBBAT8BQAAqA+kBKYEtASkByAAVo0ACPQElAL+D5QB9AoACPgH1gMcCPQLAAhYjSAELgSeBV4EXgJeA0gAbAJeBJwFJAQkAFqNAAj8DeQD/AAACFoF/A9YAfgPXgX4CUAAW40AAKwIvAj8BbwEvgK8ALwE/AW0BCQJAABejSAALgicC04IfARAA24AXAROBJwFLAgoAGCNAAB+BAID+gACAn4AmAd/BXwFfwX8BwAAYY0AAPwN9AP8DAAA+AcOAOwPzAvIC9APAABijQAM4gO+Cv4H3gw/A/4M3gc+Af4PBgQAAGSNIAKkAaQIJAbkAT4IJAjkDyQApAAgAwACZo2ACSQE5AM+COQHJACACTQEygKIA3gECAhrjYAJJATkAz4I5AekAIAH5AE+COQHJACAA3CNIAgoBKgDKAQoBPwPKAkoCSgJKAkgCAAAdI0QBtQBFAL+A1QEVARQBAAE/gUIBBAEIAR1jRAG1AEUAf4DVARUBAAFzAQwBEwEggUABHaNIAykAyQE/gekCKQIAAgkCPwLJAgkCAAAd40QCNQHFAL+B5QIlAgACOQJJAokCjwKgAmBjRAG1AEUAf4DVARQBAQFUgUiBaQEiAQIBIWNAAjYBxgE/AeYCBAI1AtMCmQKZArcCwAIio0gDKgD/AeoCAAI+AmICAgKfgnICioKAAiLjSAGqAEoAv4DiAQgBBgFVgVUBVwF9AUABJWNIAaoASgC/AOoBQAFXATcBNwF3ATcBAAEmY0gDKgDKAL8B6gIAAj0CzAIvAiQCvQJAAifjQAIpAckAv8HpAgECPQLkAheCZAI9gsACKONFAAUBJ4D9AMQBP4EqgT+BQAE5AUcBQAEqI0QDNQDFAL+B5QIpAjvC14JSgluCN4LAACzjQAAAAg8BKQDJAQkCOQPJAkkCTwJAAgAALSNAAjeBxIE8geeAAAO/AEAAAQA/AAABwAIvo0ACN4HEgTyB54AAAj4DwAI/g9ACEAIAADDjQAI3gfyB5IEngQAACQMIgP+ACIDIgQgCMuNAAieDxII8geeDAAICAf/AEgLSgbKCUgAzI0ABN4HEgTyA14CAAhOBEgD/gBIA0gESAjRjQAE3gcSBPIHngQAAPoHtAj0CgQK/AkABNuNAASeDxIE8geeAAAM+APICX8GSAXYCAgI3Y0ABNwHFAT0A5wCAAD8DxQFFAUUBfQFAATfjQAI3gcSBPIHngAACPwPVAjUAVQGfAUACeGNAASeB/IHkgQOAuAM+AMOCPgHCADoAQAA6I0ABJ4H8gfyB44EIAC4A6gKrAq4CqgGqADqjQAAHgfyB5IEPgz4BxYA1AdUCFwK0AkQBO+NAAjeBxIE8geeBJAAiA/WCKQIvAjED4AA840ACN4H8geeBAAA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dAV0BfwPIASTjwAAfAJGAvQPIAD4D7QC8g+UARgI8AcAAJWPRAB0Ak4C5A9AAhAC9A60Cb4JtAJ0BRAJlo8AAHwBRgH0B0ABFAC0B7QF9gW0BZQHgACZjwAAdAJOAuQPFADuB7QK5A90BIoDeAQACJuPIAAkASwBPAEkAecPJAE0ASQBJAEgAAAAnI8AAEIFXgVWBdYFVgV3D1YF1gVeBQIFAACejwQAkg+SBP4EkgcQAEQBdAHGD3QBRAFAAJ+PAAT8A6QPpAS8BwAAWAFIAc4PaAFIAUAAo48AAEQJVAnHB3QBBAjwBJQC/g+UAvQEAAimjxAIzAfoAUAIEAf+AOAPCABaAcYPaAEAAKiPRABVCcYHdAEACHAG/gEAAFQBxw90AUAAqY9EAFUJxgd0AUAADgjkBwAAVAHHD3QBQACrjwgAWgnsB0ABHATyBIgEAARVAccPdAEAAK2PBADMD1IBegfWANYPAABUAccPdAFEAQAAr48IAFoJ7AdIAUgPawVoBwwAXQHvD0wBAACwjwAAAAz8A0QA1A9UCFQE1AFUAlQFVAlACLGPAACAAn4CIgbqBqoCKgpqCqoG6gJiAwACso8ADPwDbAnsCf4H7AnsBf4H7AXsBfwLQAm5jwAIIgTsAwgEAAgIC8gIPggICggK+AkABLqPQghGBMgHAAQECMQLPAgECgQKBAr8CQAIu49ICFIE1gMEBBAIEAgQCP8LEAgQCBAIAAi8j0IIQgTMBwAIAAqCCWIIPgjACAAJAAoAAL2PAAgkBOgDAAQECAQKBAr0CxQIDAgECAAAvo8gCCIE7AMABAgICAvICD4ISAiICAgLAAjBjyAIJgToAwAEJAgkCCQI/AsiCCIIIggABMKPAAgkBOgDCAQgCCQKJAr8CSQIJAgkCAAAxI8gCCQE6AMABDAECAmsCmgKKAooCggJAADFjyQIJAToBwAEJAgkCPQLJAgECPwJAAqACcePIAgiBOwDAAQICCgIyAgICggK/gkICAgIyI8kCCQE6AcACAQLxAg8CBQIFAoUCvQJBATOjwAIRgzIAwAE/AkECQAI/AsECAQJ/AgAANCPIAgkBOgHAAgQCtQJNAkUCVQJlAkUCgAI0Y9CCEIExAcACAAL/ggSCBII8gsSCBIIAADUjwAIQgTMBwAIAAv+CDIK0goSCdIKMgoCCtiPAAgiBOwDCASACEQIJAj8CwQIJAjECIAI2Y8ACCQE6AcACCgKKAnKCEwIuAgICQgKAADbjyAIIgTsAwAESAhIC/4ISAhICP4LSAhICNyPIAgkBOgHAAgQChQJ9AgUCPQLFAqQCQAA3Y8ABCIE7AMABFQEVARUBP4FVARUBMQFAADejwAIIgTsBwAEFAksCSYJ9AskCSQJBAkAAN+PIAgkBOgDAAQACfwIJAhkCOQIJAk8CgAI5o8ACE4EwAcEBfwEBgj8CwAI/AsECvwLAAjqjwAIJAToBwAI+AtICkgK/gtICkgK+AsAAOuPRAhEBMgHAAQACPgLSApOCkgKSAr4CwAI8I9CCEIEzAcABIgJaAgICP4LCAhqCIoJAAj0j5AI1AS0AwAE/AkECvQKlAr0CgQK/AsAAPePQghGBMgHAAgkC6gIYAj+C2AIqAgkCwAK+Y8ACCQE6AMIBGAICAv6CAwK+AsICOgIAAD9j0IIQgTEAwAE/AlUClYKVQpUClwKwAsACACQQAhCBMwHBAgACv4LKgpqCKoJXgpACgAAAZBCCEIEzAcABEgITgpICfgISglKCkgKAAgCkCAIJgToBwAEFAjUC1QKfApSClIK0gsQCAOQAAhECMgHAAScCEAK/gkACP4JQAqcCoAKBpAACE4MwAMIBOoIjgqICvgJiAiOCOoICAgJkAAIJAjoBwAIIAqsCWgIPgjoCygKKAogCQqQAAgiBOwHAAQiCPoLRggwCv4LAAg4CMAADZAAAJII9AaUBQAI9gsQCF4JEAmUCvILAAgPkAAIJAjoBwAELAgsC9wIXAicCqoKqgkgCBCQAAgiBOwDAAQSBaoIrgpSCuIJUgiKCAAEEpAgBCIE7AMABHQFVwXUBPwFVgRVBdwEAAQUkAAITgTABxAESAlUCFQK8glUCEgJUAkQCBeQAAgkDMgDEAQECHQK1ApUCtQL9AoECgAIGZAAAFQEfALYAwAE+AV4BXwFeAX4BQgEAAQakCAIIgTkAwAE+gmuCK4I+guuCK4K+gkAABuQAAgkBOgHQAguC/QIAAokCvwLJAokCgAAHZAACCYI6AcACEgK/gkICPwLEgjyCxIIAAAekAAA0gi8BpgFAAi8CrQK9Au0CrwKAAoAAB+QQghCBMwHAAh0ClQJ1Aj+C9QIVAl0CgQKIJAACCQI6AcABCAIrAuoCrwKqAqoCyAIAAAikAAIJgTgBwQIlAqSCq8K6guuCqoKCAoQCCOQAggmBOQDAAR8CVwJXAn+C1wJXAl8CQQJLpAACE4IwAcQCJQKVApUCP4LVAl8ChAKAAgxkAAIQgzMAwAE/gkCCOoJegnqCQIK/gkACDKQAAhGCMgHAAQQCP4LqAr8C6oKqAoICgAIOJBCCEQExAcQCHgKVgnWCHQI3AtQCnAKAAE7kCAIJAToAwAEnAhUCnwKVAlcCdQIHAgAADyQJAgoBOgHgAt8BWwN7A9sDWwN/A8ACAAAPpAACCIM4gMIBOgF/Aj8CwsI6ggEDOgPCABCkAAIRgTIAwAEKAluCdgIqArICa4IqAgICUWQQAhEBNgDAAX8CBQI1AnUCdQL9AncCQAIR5AACCQE7AcACP4Lagj+CX4Jagt+DMAPAABKkAAIRgzAAwQF/AgXCuQJDAhGCvQLVAhECEuQAAgiDOQDAAR6CXoJegn6C3oJegl6CQABTZAACCIE7AMABfwI1Au0CPYLtAj0C7wI4AtOkAAIQgjMBwAI4As+COIJegnqCT4I4AsAAE+QAAQiAuwDgALcBTwFvAW8BTwFPAf8BwACU5AgCCQE6AcABAgI+gu+CrgKvgr6CwgIAABUkAAIRgTIAwAE1An0CdQJ3gvUCfQJ1AkQCFWQAAhECMgHAAj8CtwL3AreCtwL/AqICgAIV5AACCIE7AMABO4IagpqCX8IagrqCi4IIARZkJAI9gaQBQAIuA6kDrwO9g+sDqwOpA6ACFyQAADRDLYCEAVCBPoJBgpcCPYLQgjBCgAAXpCQCPMEFAOABH4IAgvyCD4Kfgh1CXUKMAFgkAIIQgjCBwgEfAlcCdwI3gtcCfwJiAoACGOQAghCDMQDEAT8CfwK/Ar+CvwK/AqQCwAIZZAACCQM6AMABEwJfApkCuwLZApiCmoLQABokIAI1ggwBwQG9Al+DVQNRAsQCI4LeAoICmmQAAhGDMgDAAT0CTQI/Al2CfwJNAr0CQAIbZAACBEE9gMABP4Jvgq/Cr4Kvwr+Cx4IAARukAAEJgLoA4ACfAQUBXQEVgZUBXQEFAUAAHKQkAjUBLQHAAb8CQwItAqMCtwLjAq8CgAKdZAACCYI6AcABHwJbgtsCWwJfA3+D3wJBAF3kIAI1AS0AwAF/AjsC/wM7A78D+wMfAkACXiQAQgmBOQDgASuCu4K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SgJABAAAyJEAAJYBUgD+B1UAAAb/ASEA4QAhAz8MAATKkQAAKgOiAP4PowCqAKACrgKSD6oCpgIiAsuRBACsAfwPogCoAD4C+gK+AroHvgL6Ai4CzJEAAAAEvgSqBKoEqgT+B6oEqgS+BAAEAADNkQAAEAT0BfQF9AX8B/QF9AXyBfIFEAQAAM6RAAB8CVQJ/AdUBXwBAAgkCPQPLADkAAAAz5EAACAE/Af8B/wH/Af8B/wH/Af8ByAEIADRkRAAsAiQCZgLlgjxD5IIlAiYCpAIMAgAAN2RUAhYC1QI8gdUBEQFAAAgAP4PIAAgAAAA45FQCFgLVAjyB1QERAUQAM4ICAkIDPgDAADnkUAIWAtUCPIHVAYADP4DAAD8BwAA/g8AABWSUAhYCfYHVARUAQAIRAj8D0QIxA98CEAAHpJQCFgLVAjyB1QEWAUGASUJJAkEBfwDAAA0klAIWAtUCPIHVAUQBEgA1w9SAEwE0AcQAESSUAlcCPIHVAREARAETgRIA/4ASANIBEgIcZJQCFwJ8gdUBAAN+AMIAAgH7gQIBIgHAAh0kkAIXglAC2AJXgnQD1YJZA00CVQJRAgAAICSUAhYC1QI8gdUBwAA/g9KCMoBSgZ+CQAIg5JQCFwJ8gdSBAQNZAQ0AywAJgC0DzQIRAaFklAIXAnyB1IEhAT+DwIA6gMqAcII/gcAAJiSWARUBfIHUgREBRACGAGUD+YEnASEDwAArZJQCFwF8gdUBFQBAAioBP4FqAaqCaoIAAazklAIXAX2B0AFEAD8BJIGkAGSD/QIGAgQBLeSQAhQCVwE8gdEBggN9gcQAF4BEAn2BwAA0pIAAFAIXAXyB1QFAABIBa8Fkg+uBaIFQATqklALWAT2B0gEWAUIAOgPqAL+D6gC6g8AAO2SUAlYBVQE8gdUBAABegzMA0gAzA96CAAG/JIAAFALXAjyB1QH/AsMBKQD5AMMC/wHAAAEk1AIWAn2B1QERAUABNgCVgjUD3QATAVABSKTUAhYBfYHVARUAQAIKAmeBRgHWgcqC6gIJpMoBC4F+QMqBioHgAe+AOsPqgCqBL4DAAArk1AIWAlUBPIHRAcABL4E6gOqBqoJvgcAAC+TWAhUCfIHVAQEBSQA/gdkBWQFfgXkBwAAMpNQCFgJ9gdUBAAFogQqAOoPqgEqAr4CoAQ2k0gLXAjyB1QERAEUAFQP1Ah+CNQDVAVACUqTWAhUCfIHVAQEBfQEtAKUAf4PtAH0AgQES5NQCVgI9gdUBQQA4A8+ALIDqgI+CeAHAAB1k0gIXAnyB8QHMAwuA+AEqgb/C6oKvgoICH6TaAhsCWoE+QdqBwIM/giuCv4Prgr9CgAIjJNIBM4E+QdKBewFrQL+D6wA/A+vAvwEAACWk1AIWAhUCfIHVAQACfQLsAq+ArAC9gsACK6TQAhQC1wI8gfUBQAA/AvsCu4C7Ar8CwAK4ZNQCFgIVgnxB1IECg3qCG4HawFuD+oJCAQYlFAIXAXyB1QFAADyC3oLcwvzD3ILfgvyCzWUMAhaCvwHHAd0ALQHvgdUDP4FEAfWCAAGUZRQCFwJ8gcEB34Aqg/uCboPAAlOD0gJSA9wlAgIXAnyB0QFCADoD8QHkgL0D4QCaA8AAH2UUAhcBfIHVA0ACBgI3gXcA9wL3gusCygAiJSYAIYI9Q+UBIQEIAAgACAA/g8gACAAIACJlAAAmACGCPUPlASUBAAABAgECPwHBAAEAJOUAACYAIYI9A+UBKACHABKAIgJCAj4BwAAmZSYAIYA9Q+UBJQEAADyAIIA/giSCJIHAgCdlJgAhgj1D5QEAAToAQgBCAH+BwgJCAnoDZ6UmACGCPUPlASEDIAIOAgABP4DgAEIADAAn5SYAIYI9Q+UBAQE8AGQAJAA/g+QAJAA8AGglLgAhgjlD6QEBADwDxACkAF+AJAJ8A8AAKKUmACGAPUPlAQAAP4PAgIyAfoBAgj+BwAApZS4AIYI5Q+kBKQAAAz8AyQBJAkkCfwPAACmlAAAmACGCPQPlASEDDAIDgboAQgDOAQICKeUmACECPYHlASEAhAATgJICUgJCAz4AwAAqZSYAIYA9Q+UBAQEEAHuAQgJyAsICPgHAACulJgAhgj0D5QEhAwACEIO/glCCEIO/gkACLGUmACGCPUHlAQAAsgISAT+BEgHKgmqCQAEs5SAAKwI6geoBIACEAD8D5AEkASQBPwPEAC7lJgAhgT1B5QElAIEAIAPgAT+BJAEkA8AAL6UmACGCPUPlAQABP4BkgCSAP4PkgCSAP4BwZSYAIYI9Q+UBBQEwADODMgC/gFIAkgEQAjDlJgAhgj1D5QEhAQQAEgBVgJEDcgAEAAAAMWUmACGCPUHlASUAgAAvg+CBIIE/gTADwAA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AAT+AtAI3gfQAPYCAASblgAA/w8ZAO8AbAa3AK4EoAenALgCrgIiAJyWAAD+DwIAOgHGABAC9AL8AvYP/AL0AhACoJYAAP4PMgDOCQQEbAFkDWwJZAtyCeoBAAyjlgAA/g8yAM4AAAnuBZgDvwCYA+oPqAIAAKeWAAD+DzIAzAhABLYHgASqCfgMyAvOCCgJqJYAAP4PMgDMCOIHFATUC+wI7gl8DewPBAiqlgAA/g86AcYIUAS4AvQAEwj0BrgE6AwIALGWAAD+DwIB/gDADfYB8gf6C/IN+QH1DaAItpYQBFQFVAVUAlQK/gdUAVQCVAO8BJAEAAC7lhAIEAl4CXwJeAd4BXwFeAV4B3gJSAgACL6WAAAUBqQB5AAcAyAA/g+oBPoHqASoBAAAwJYAAFAAWADED+AK3grwCvAPxArECtgKEADBlgAG/gFCAPoHAgAiAPoHUgX6B1IFUgUAAMSWCAzoAx4IyAcIBCAD/AeqBKgE/AeqBKgExZYkBrwBZAj8ByQA8A+uBKgE/geqBKgEAADGlggICAV8BV4DXAPcD34BXQNcBVwFRAkAAceWAAAEDvwBrADsD+wK7ArsD+wK7Ar8CgQIzJYAAPAPAAT+BwAE/gcwAv4PqAT6B6gECATPlhAAuASmBLQE7A8gAPgPrgSoBPoHqAQIBNGWJAC0BI4ChA+8AkAEMAD+B0gF/AdKBUgF1ZaAD3wANAe0AjQJ/AcwAPwPqgT0B6gECATWlgAA7gSqBPoDrgLgBjAA/g+oBPoHqASoBNmWAAAICL8EvAS+BbwGiAK+ArwFvgW8BCQE3JYgAKQGlADGD7QCAAD+D6gEqgT6B6gEqATelgQIfAl8BaoDUg04AfwPqgSoBP4HqAQIBOKWgA/0ANQD1gLECPQHIAD+DygF/AcqBQAE45YAAPQJ1gX0A9YF9AUgAP4PqAT8B6oEqATolgQA9A8UAFQBFAD8DxQAVAFUChQI9A8EAOqWGADKCsoKygqKCr4KigrKCsoKyg8YAAAA75YACJgIygjqCcoGngTKBIoG2gnKCJgIAAjwlgAKGAmaBboFCgM+ARoJOgmKBxgBAAIAAPKWAAEMBWUFZQdFBV8FRQVlB2UHBQ0cCQAA85YcDMYDdgh2CX4HRgF2CXYJdglGCVwHAAD2loAAmACaAboFSgXeBcoFWg26C4oBmACAAPeWGADKD8oKygqKCr4PigrKCsoKyg8YAAAA+ZaAAJgA+gf6CuoK/grKCvoLegrKCRgEAAD7lgAAGADaB/oCygLeB9oK+grKCxgIAAYAAP6WAAFYC1wLXAvMBtwC3AJ8C0wLXAcAAAAAAJcAAFgAWg96AUoP3gFaD3oBSglYB0AAAAAElwAAHgAOCK4HhgHeBYYG1gLeCoYHHgAAAAaXAAhMCeYFdgsmCD4NRg32D3YNZg0MDQAAB5cYDNoDegn6B8oF3gXKB/oF+gXKB1gLAAkJlwAADAFmBeYG5gbGB94Gxg7mDuYHTAUAAAqXAAg4CYoLqgmKD74Jig+qD6oJCgs4CwAIDZcAAQwBpg+2C4YL3g+GC7YLpguMCAAAAAAOlxgCWgtKC2oLyg9uD0oPWgf6B0oLWgsYCBOXAAAOAA4I1guuCq4GBgKuBr4K5gsOCAAEFpcAAJgEigK6D4oCPgCKBroBug+KApgEAAQclwAAmAS6AroPugI+AL4H+gX6BfoFyAcAAB6XGADKD/oF+gXKBd4MCgj6C/oFygXYCwAIJ5cAAAAMTAPmD/YBRgMeCsYGdgPmC0wHAAEylxgIyg16CXoPygueAooOegv6CkoPWAIAADiXAABMB+YH9g/GB14DBgz2A/YC5grMBwAAOZcABuwBdQdtBW0FBQcvAuUCpQ+tAu0CoABClwAO7AFlBP0E/QZ1BOcHZQb9BvUG9QQgAEiXAAgMCeYL/gtGCa4PpgnmDU4LtgjkCAAAUpcQABQA/A+8ArwCvgK8ArwKvAr8BxQAEABWlwgC6AIOAugBAAD8D7wCvgK8CrwK/A8UAFmXEADcD9wC3grcDxAAWAlWCfQHXAHwAUAAXJcQCNwH3ALeCtwHAACIAqQK6geiAuoDgABelwABKAEoASgB/g8AAAAA/g8oASgBKAEAAWCXAAQIB3wHXgdcD1wAXgBcD1wHfAcIBwAEYZcADvwBBAQ0B3QHVA8GADQPdAc0B1QHAABilwAA8g8SBPIHXgVSBfIHEgQSBPIPAgAAAGmXAAAEAuQCvgK0AvQPtAK0Ar4C5AIEAgAAdJcEAu4C/A+uAuQCAAIwAP4PAAD+BxAICAZ2lwQA7gKsAvwPrgJAAP4HQgh+CEII/ggABouXAADkAq4C/A/uAgQKIAgkCb8PJAkkCQAAjZcEAO4CrAP+DuQCAABICcYJdAZEBegEQAiRlwQC7gL8D64CZAjgBwQIkAt+CNAIEAsAAJiXBAD+ArQC9A++AkAI9AcQAF4BEAn0BwAAoJcEAv4C9A+0Au4C/AaKA+gP6AGICvgHAACtlwQC7gL8D24A/g98CawH/ASsCPwIBAgAANOX/AKsAq4PrAb8BgAE7AeuBawPrAXsBQgE5pcAAJIAkgCSAJIA/w+SAJIAkgSSBIADAADnl6gAqAD8D6gAqAIAAXQMhAN8CAQI/AcAAOmXAAD0AtQC3g/UAvQCAACoAP4PqACoBIgD7ZcAAFAFUAVQBf4HAAQABP4HqASoBKgEAADzlxAAFADUB1QFVAVWBVQFVAVcBdQHFAAQAPWXIADoB2YFdAXkByAAEAEuBagICAj4BwAA9pcQANwHVgVcBdQHBACwB44EogSiBJ4HAAD7lxAA3AdVBVoF1AcEAPgPVANUA1QD/A8ACP+XlAC+ANUH4Af+B+4HzgfAB94HxgeaAAAAAZgACAQI/AusBqwGrAKsAqwGrAb8CwQIAAACmAQIBAj8DwQAAAj8C6wKrAKsBqwG/AsAAAWYBAEEAfwBBAEECPwHrAasAqwGrAb8CwAIBpgACPwHAAD4AwAA/g8AAPwHrAasAqwK/AsImEgIyAQkBBADBAj8C6wKrAKsAqwK/AsACBCYJAgkCPQPPADkCAAI/AusAqwCrAr8CwAAEZgAABQH9AAUAvQDAAj8B6wGrAKsAvwLAAgTmOgAiAD8D4gE6AIACPQHtAa8ArQK9AsAABeYAA74ASgMvAKoA1gI/AusCqwCrAb8BwAIGJgAAFgA1AdSAMQJDAT8B6wCrAKsBvwPAAAtmAAAfAhMBUwEfAcADPwJrAqsAqwG/AcACDuYEADeBRAEfwIUAZAA/g1WA1YDVgH+BQAEPJgAAHgGWAH8D1gBOAr8CawGrAKsBvwHAAhGmAAEvASsAvwPrAKcCPgJrAqsAqwG/AcACEyYQAxcA1wE3AdcCQAM/A1cCVwJXAn8DQAMTZggAawPtAVWBaQHDAD8B6wGrAKsCvwLAABPmAAM7AM8CK4JLAUkAvwLrAqsAqwG/AcACFSYAAzkAywIpgosBSQA/AusCqwCrAr8CwAIWJgAD/wAfAasAKwP/AEACPwHrAasAvwLAAhbmAQK/AvcCt4C/AsAAPwHrAasAqwG/AsAAF6YAACsBJgC3gGcAogI/AusBqwCrAL8BwAIZ5gADvoBKg+qB9oHgAcCCPoLrgaqAvoHAgxvmAAB/A3aARoJ+gO8AwAJ/AesBqwC/AsACHWYAAAECPQLFAgUBBQC3AEUBBQI9AsECAAAdpgAAAQIBAj8BwQAAAj6CQoE7gMKCPoJAAB3mAAAAAD8AxACEAEACPQFFATcAxQE9AkECHmYBAEEAfwBhAAACPoFCgQKA+4ACgIKBPoFepgADPwDAAD4AwAA/g8AAPwIDAbsAQwC/Ax7mAAESAQmApABAgj6BAoC7gEKAAoC+gQCCH2YAAASBvIBEgDyBwAC+ggKBu4BCgD6DgAAfpj8DwIA+gcKBPoCAAj6BAoG7gEKAPoOAAB/mAAA9ACEAP8PhARgAvoICgbuAQoA+g4ACIGYMAwMAuIJIAjvBxAA+gkKBO4DCgj6CQAAgpgwBAwD4gIAAt4HAAD6DAoC7gEKAPoNAASEmCIAKgjqBzYA4gAACPoECgbuAQoA+g4AAIWYAAjwB5AAngD0AQAI/AkMBOwDDAj8CQAIhpgQAFABTAJqDdAAAAj0BBQG3AEUAPQOAAiHmAAO/AEkDD4D5AYMAPAEFAbcARQA9A4ACIiYAACiCJIIige2BAAM+gkKBO4DCgj6CwAAiphIBFgESAP+AHgDSAjgBBQC3AEUAvQEAACRmCAAuAsgCPwFKAMgCPQJFATcAxQE9AkAAJOYAAAsCKwHvACqBwAE8ggSBt4BEgTyCQAAlpgABM8C1AHUD7IBAAryCBoG1gESBPIFAAiXmAAE/ATUAvwP1AH8AOAJFATcAxQI9AkAAJiYQAxcA1wE3AdcCQAM9AwUC9wIFAr0DAQMnJgACPIHGgGzBKoCEgj6BAoC7gEKAPoOAACdmKwAFAZeBXQFlA4cAPQJFAbcARQA9A0ABKCYBAr8C+wK7gL8CwAA9AkUBNwDFAj0CwAApJgACPwL/Av+C8wLPAPwCAoE7gMKCPoJAAComAAAAA78AQQE7AUsBfwHLAXEDPwHAAgABLGYAA7+AUIE+gNCAv4AAAPYBFYJUAncDQAAxJgEAPQN/AH8B6QP/gdmBPYD+gP6BwQIAATGmEAGygFqAGcG6wFiDP8DYQT9A2EH/wEABs6YAAAADv4BAgQaAqIB4gAaBwIA/gcACAAG0pgoBMoEDgLsAwAK/AcEAvQBBAL8BwAMAADYmHQN1AH8CdQH/APUASQN/AMUBuQB/AcADNuYUAhSBvoBQgBCAPoPQgBCAM4DFAUqCUgF3pgAAAQABAAEAAQABAAEAPwAIAdQCIgIiAbfmAAAEAAQCPgPtAiyCbQCtAb4CZAIEAgAAO+YCATkB9IE0gT0BgAO/gFiDKIFIgPiBAAI8pgIBPQHsgSyBPQGIAAcDIoD+AAIAzgECAj8mBAE/AeyBLIE9AYAAOwDLAHsCQQI/AcAAP2YCAAIBPwHugS6AvQGAAD4A5YE9AQEBPwE/pgQBPwHsgS6BPIGNAToASYA/A8kAOQDAAAFmQQA8g9aCVoF8gkAAIoM+gOIAP8PiQAAAAqZgACUAFwI/g/cCdwJ3AfcBf4NXAiUCAAAEJkQAJAAqAjuD9wLgAO2B9YHlgWuCKIAAAATmQQI8gdxBnUDIQSJBP8DWAAfAvgD2wYQABiZCAjsD1oJVgX0DQQAlAKUCPMHlACEAogEKJkQBPgHtASyBOwGHAD0D5QFlgWUBfwHAABSmQAA8A++BLIE9A5AAGoNegNvASoP+g0oBWWZMAAMCOoPCAQ4AAAO/AEEAAQA/A8ACAAGbZkwAAwIygcYAAAO/AE0DNQCFAPyBBIIAABumXAADAjqDwgEOAIACDAEDgPoAAgDKAQYCHCZIAAUAMoPGAIAAOgDJwAkAPwPJAAkAuQBcZlgABQAyg8YBEAA8AcsCSgJ6AsICvgJAARymTAADAjKBxgCCADiAyoBKgHqCQII/gcAAHWZcAAMCOoPCAQwAAQE/AeUBJQElAL8DwQCdpkgABQIygcYAggIoASuA7gApA/UCJQIAAR6mTAACgjlBwQCCApgCBQEhQOGAwQEdAhABHyZIAAUCMoPCAS4AoEI+geIAIgA/A+LAIAAf5kgABAAzg8YBIAClAj8DxAE/gKQBVYIAAaAmSAAHADKDwgEGACQBpgAlAjyD5QAmAKQBIGZIAAUCMoHGAIACFQJxAVsAkQDwgRaCEAAhZkgABQEygcIAjgA0A9IBQYFRARcBcQPAACGmSAAFATKBxACBAD8B/QHVQVWBWQFDAcAAIuZMAAMCMoHGAKQALgCbgQ8BWwJrAq4AIAAjZkgABQEygcYAwAIdAV2BXQD9AF2A3QFAAiPmTAAjg8oBBgEQAA+D7AK5A+cCoQKvA8AAJKZEAAcAOoHCAFgBN4F/gbeAv4G3gX+BAAAlpkAAAQA9A9XBVQFXAVUBVQFVwX0DwQAAACZmZAAVABUD7QKlAq8CpQKsgrSCtIPUACQAKiZAAC8ArwC/g78D/QP+A72DvIP/g7YAgACrJkACAAM/AFUAVQFVAH8DVQBVAtUCQAHAACzmQAG/gCqB/4CqgoAB/wHEAj+CRAI+AgABsGZAAb+AKoC/gmqC6oHAADWDAgDDgXRBBAIxZkABv4AqgP+C6oOgA3+AyIA4gAiAz4MAADGmQAG/gCqBv4CqgsAB/4PAgSiBXIEigUABdCZAA7+AKoG/g6qCaoHAACKCPoPjAiICAAI0pkABv4Aqgf+AoIKEAfMASoB6AkICPgHAADVmQAAFAjMBeYB5AXsAeAD/gHyA/IFHgMAANuZAAN/ANUDfwNVBcEDHAwkA/8CJAQ8BAAI3ZkABv4Aqgf+A6oPAAAYAPgHjgiICFgIQAb/mQAG/gCqBP4Bqg8AADwHigS4Bq4JIAgAAA6aAAb+AKoD/guKByAApAe0Aq4LNAjUByAAEpoABv4Aqgf+A4oPQACiCWYJ+g9aCeYNIAgTmgAG/gCqA/4GqgoKB+gIJgXyAyQH6AkICBmaAAb+A6oB/gmAD/wH7APsB+oDagX6BwAAMJoADP4Dkgj+B0QA9gXcC9YL3Af2C1QGQAA3mgAO/gCqAv4LqgcAAPQJagnSD2oP5g8gCEWaAAb+BqoB/gmCBwAA/AdUCuwLnAnECwAAVZoABn4H/gKqCQIHqA/6A94F2gf5AKkPAABXmgAG/geqAf4NggNwCLgG9gBUDLgC0AwAAFqaEAg6DP4D+gP6C9oDyAvsA9YLrAssBiQAX5oADv4Aqgf+DgAHfga6AP4PbAAUAywEAABsmgAAAAIEAnQCRAJEAkQCRAp8CEAHwAAAAG6aAAJ6AkIJQgj+BwAAEAyQA34AkAEQBhAIb5oAAnoCQglyCM4HAAD+DwAA/gcAAP4PAABwmgACegJCCkII/gcAAPwHEAj+CQgI+AgABnGaAAB6AkIKQgn+B/gPBAgUC+QIlAkECgAAc5oAAnQCRAlECPwHAABUDIgCiANUBBIIAAB0mgAAegJCCkII/gcAAPgPSABKAEgA+AAAAHaaAAJ0AkQIRAz8AzAISAVIAv4FSAR4CAAAeZoAAnoKQgl6CMYHEADOAykB6AkICPgHAAB7mgAA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" + } +} diff --git a/libs/fonts/pxt.json b/libs/fonts/pxt.json new file mode 100644 index 00000000000..2d31ede20c6 --- /dev/null +++ b/libs/fonts/pxt.json @@ -0,0 +1,16 @@ +{ + "name": "fonts", + "description": "Fonts for displays (V2 only).", + "files": [ + "font12.jres" + ], + "public": true, + "hidden": true, + "disablesVariants": [ + "mbdal" + ], + "searchOnly": true, + "dependencies": { + "core": "file:../core" + } +} \ No newline at end of file diff --git a/libs/microphone/microphone.cpp b/libs/microphone/microphone.cpp index e79a738fa23..cc1ea6af698 100644 --- a/libs/microphone/microphone.cpp +++ b/libs/microphone/microphone.cpp @@ -5,9 +5,6 @@ #include "LevelDetectorSPL.h" #endif -#define MICROPHONE_MIN 52.0f -#define MICROPHONE_MAX 120.0f - enum class DetectedSound { //% block="loud" Loud = 2, @@ -21,14 +18,21 @@ enum class SoundThreshold { //% block="quiet" Quiet = 1 }; +namespace input { -namespace pxt { #if MICROBIT_CODAL - codal::LevelDetectorSPL* getMicrophoneLevel(); -#endif +bool didInit; + +void init() { + if (didInit) { + return; + } + + didInit = true; + uBit.audio.levelSPL->setUnit(LEVEL_DETECTOR_SPL_8BIT); } -namespace input { +#endif /** * Registers an event that runs when a sound is detected @@ -40,9 +44,9 @@ namespace input { //% group="micro:bit (V2)" void onSound(DetectedSound sound, Action handler) { #if MICROBIT_CODAL - pxt::getMicrophoneLevel(); // wake up service + init(); const auto thresholdType = sound == DetectedSound::Loud ? LEVEL_THRESHOLD_HIGH : LEVEL_THRESHOLD_LOW; - registerWithDal(DEVICE_ID_MICROPHONE, thresholdType, handler); + registerWithDal(DEVICE_ID_SYSTEM_LEVEL_DETECTOR, thresholdType, handler); #else target_panic(PANIC_VARIANT_NOT_SUPPORTED); #endif @@ -58,12 +62,8 @@ void onSound(DetectedSound sound, Action handler) { //% group="micro:bit (V2)" int soundLevel() { #if MICROBIT_CODAL - auto level = pxt::getMicrophoneLevel(); - if (NULL == level) - return 0; - const int micValue = level->getValue(); - const int scaled = max(MICROPHONE_MIN, min(micValue, MICROPHONE_MAX)) - MICROPHONE_MIN; - return min(0xff, scaled * 0xff / (MICROPHONE_MAX - MICROPHONE_MIN)); + init(); + return uBit.audio.levelSPL->getValue(); #else target_panic(PANIC_VARIANT_NOT_SUPPORTED); return 0; @@ -75,6 +75,7 @@ int soundLevel() { */ //% help=input/set-sound-threshold //% blockId=input_set_sound_threshold block="set %sound sound threshold to %value" +//% threshold.label="threshold" //% parts="microphone" //% threshold.min=0 threshold.max=255 threshold.defl=128 //% weight=14 blockGap=8 @@ -82,16 +83,14 @@ int soundLevel() { //% group="micro:bit (V2)" void setSoundThreshold(SoundThreshold sound, int threshold) { #if MICROBIT_CODAL - auto level = pxt::getMicrophoneLevel(); + init(); + LevelDetectorSPL* level = uBit.audio.levelSPL; if (NULL == level) return; - - threshold = max(0, min(0xff, threshold)); - const int scaled = MICROPHONE_MIN + threshold * (MICROPHONE_MAX - MICROPHONE_MIN) / 0xff; if (SoundThreshold::Loud == sound) - level->setHighThreshold(scaled); + level->setHighThreshold(threshold); else - level->setLowThreshold(scaled); + level->setLowThreshold(threshold); #else target_panic(PANIC_VARIANT_NOT_SUPPORTED); #endif diff --git a/libs/microphone/pxt.json b/libs/microphone/pxt.json index abfae4823a5..bf45a199e62 100644 --- a/libs/microphone/pxt.json +++ b/libs/microphone/pxt.json @@ -2,5 +2,6 @@ "additionalFilePath": "../../node_modules/pxt-common-packages/libs/microphone", "dependencies": { "core": "file:../core" - } + }, + "searchOnly": true } diff --git a/libs/radio-broadcast/_locales/radio-broadcast-strings.json b/libs/radio-broadcast/_locales/radio-broadcast-strings.json index cd7928a6b25..6eb2af1556a 100644 --- a/libs/radio-broadcast/_locales/radio-broadcast-strings.json +++ b/libs/radio-broadcast/_locales/radio-broadcast-strings.json @@ -2,5 +2,6 @@ "radio.onReceivedMessage|block": "on radio $msg received", "radio.sendMessage|block": "radio send $msg", "radio|block": "radio", - "{id:category}Radio": "Radio" + "{id:category}Radio": "Radio", + "{id:group}Broadcast": "Broadcast" } \ No newline at end of file diff --git a/libs/radio-broadcast/docs/reference/radio/on-received-message.md b/libs/radio-broadcast/docs/reference/radio/on-received-message.md deleted file mode 100644 index 8946d0fd320..00000000000 --- a/libs/radio-broadcast/docs/reference/radio/on-received-message.md +++ /dev/null @@ -1,45 +0,0 @@ -# on Received Message - -Run part of a program when the @boardname@ receives a -message over ``radio``. - -```sig -radio.onReceivedMessage(0, function() {}) -``` - -## Parameters - -* **msg**: The message to listen for. See [send message](/reference/radio/send-message) - -## Examples - -## Example: Broadcasting heart or skull - -Sends a ``heart`` message when ``A`` is pressed, ``skull`` when ``B`` is pressed. On the side, display heart or skull for the message. - -```blocks -enum RadioMessage { - heart, - skull -} -input.onButtonPressed(Button.A, function () { - radio.sendMessage(RadioMessage.heart) -}) -input.onButtonPressed(Button.B, function () { - radio.sendMessage(RadioMessage.skull) -}) -radio.onReceivedMessage(RadioMessage.heart, function () { - basic.showIcon(IconNames.Heart) -}) -radio.onReceivedMessage(RadioMessage.skull, function () { - basic.showIcon(IconNames.Skull) -}) -``` - -## See also - -[send message](/reference/radio/send-message), - -```package -radio-broadcast -``` diff --git a/libs/radio-broadcast/docs/reference/radio/send-message.md b/libs/radio-broadcast/docs/reference/radio/send-message.md deleted file mode 100644 index e8dd9619d55..00000000000 --- a/libs/radio-broadcast/docs/reference/radio/send-message.md +++ /dev/null @@ -1,43 +0,0 @@ -# send Message - -Broadcast a coded message to other @boardname@s connected via ``radio``. - -```sig -radio.sendMessage(0); -``` - -## Parameters - -* **msg**: a coded message. - - -## Example: Broadcasting heart or skull - -Sends a ``heart`` message when ``A`` is pressed, ``skull`` when ``B`` is pressed. On the side, display heart or skull for the message. - -```blocks -enum RadioMessage { - heart, - skull -} -input.onButtonPressed(Button.A, function () { - radio.sendMessage(RadioMessage.heart) -}) -input.onButtonPressed(Button.B, function () { - radio.sendMessage(RadioMessage.skull) -}) -radio.onReceivedMessage(RadioMessage.heart, function () { - basic.showIcon(IconNames.Heart) -}) -radio.onReceivedMessage(RadioMessage.skull, function () { - basic.showIcon(IconNames.Skull) -}) -``` - -## See also - -[on received number](/reference/radio/on-received-number) - -```package -radio-broadcast -``` \ No newline at end of file diff --git a/libs/radio/_locales/radio-jsdoc-strings.json b/libs/radio/_locales/radio-jsdoc-strings.json index 3c1c15c5f57..3431037c1c9 100644 --- a/libs/radio/_locales/radio-jsdoc-strings.json +++ b/libs/radio/_locales/radio-jsdoc-strings.json @@ -8,6 +8,8 @@ "radio.Packet.time": "The system time of the sender of the packet at the time the packet was sent.", "radio._packetProperty": "Gets a packet property.", "radio._packetProperty|param|type": "the packet property type, eg: PacketProperty.time", + "radio.off": "Disables the radio for use as a multipoint sender/receiver.\nDisabling radio will help conserve battery power when it is not in use.", + "radio.on": "Initialises the radio for use as a multipoint sender/receiver\nOnly useful when the radio.off() is used beforehand.", "radio.onDataPacketReceived": "Deprecated. Use onDataReceived() instead\nRegisters code to run when the radio receives a packet. Also takes the\nreceived packet from the radio queue.", "radio.onDataReceived": "Used internally by the library.", "radio.onReceivedBuffer": "Registers code to run when the radio receives a buffer.", diff --git a/libs/radio/_locales/radio-strings.json b/libs/radio/_locales/radio-strings.json index 85346e90b51..580966da7cf 100644 --- a/libs/radio/_locales/radio-strings.json +++ b/libs/radio/_locales/radio-strings.json @@ -6,13 +6,23 @@ "radio.onDataPacketReceived|block": "on radio received", "radio.onDataReceived|block": "radio on data received", "radio.onReceivedBufferDeprecated|block": "on radio received", + "radio.onReceivedBufferDeprecated|handlerParam|receivedBuffer": "receivedBuffer", "radio.onReceivedBuffer|block": "on radio received", + "radio.onReceivedBuffer|handlerParam|receivedBuffer": "receivedBuffer", "radio.onReceivedNumberDeprecated|block": "on radio received", + "radio.onReceivedNumberDeprecated|handlerParam|receivedNumber": "receivedNumber", "radio.onReceivedNumber|block": "on radio received", + "radio.onReceivedNumber|handlerParam|receivedNumber": "receivedNumber", "radio.onReceivedStringDeprecated|block": "on radio received", + "radio.onReceivedStringDeprecated|handlerParam|receivedString": "receivedString", "radio.onReceivedString|block": "on radio received", + "radio.onReceivedString|handlerParam|receivedString": "receivedString", "radio.onReceivedValueDeprecated|block": "on radio received", + "radio.onReceivedValueDeprecated|handlerParam|name": "name", + "radio.onReceivedValueDeprecated|handlerParam|value": "value", "radio.onReceivedValue|block": "on radio received", + "radio.onReceivedValue|handlerParam|name": "name", + "radio.onReceivedValue|handlerParam|value": "value", "radio.raiseEvent|block": "radio raise event|from source %src=control_event_source_id|with value %value=control_event_value_id", "radio.receiveNumber|block": "radio receive number", "radio.receiveString|block": "radio receive string", @@ -21,6 +31,7 @@ "radio.sendNumber|block": "radio send number %value", "radio.sendString|block": "radio send string %msg", "radio.sendValue|block": "radio send|value %name|= %value", + "radio.sendValue|param|name|defl": "name", "radio.setFrequencyBand|block": "radio set frequency band %band", "radio.setGroup|block": "radio set group %ID", "radio.setTransmitPower|block": "radio set transmit power %power", @@ -28,5 +39,8 @@ "radio.writeReceivedPacketToSerial|block": "radio write received packet to serial", "radio.writeValueToSerial|block": "radio write value to serial", "radio|block": "radio", - "{id:category}Radio": "Radio" + "{id:category}Radio": "Radio", + "{id:group}Group": "Group", + "{id:group}Receive": "Receive", + "{id:group}Send": "Send" } \ No newline at end of file diff --git a/libs/radio/pxt.json b/libs/radio/pxt.json index 3eb5331a296..83d6a9e20c2 100644 --- a/libs/radio/pxt.json +++ b/libs/radio/pxt.json @@ -8,5 +8,6 @@ } } } - } + }, + "searchOnly": true } \ No newline at end of file diff --git a/libs/radio/shims.d.ts b/libs/radio/shims.d.ts index ebda995f475..6df6da4284b 100644 --- a/libs/radio/shims.d.ts +++ b/libs/radio/shims.d.ts @@ -5,6 +5,20 @@ //% color=#E3008C weight=96 icon="\uf012" declare namespace radio { + /** + * Disables the radio for use as a multipoint sender/receiver. + * Disabling radio will help conserve battery power when it is not in use. + */ + //% help=radio/off shim=radio::off + function off(): void; + + /** + * Initialises the radio for use as a multipoint sender/receiver + * Only useful when the radio.off() is used beforehand. + */ + //% help=radio/on shim=radio::on + function on(): void; + /** * Sends an event over radio to neigboring devices */ @@ -44,7 +58,8 @@ declare namespace radio { //% help=radio/set-group //% weight=100 //% blockId=radio_set_group block="radio set group %ID" - //% id.min=0 id.max=255 shim=radio::setGroup + //% id.min=0 id.max=255 + //% group="Group" shim=radio::setGroup function setGroup(id: int32): void; /** diff --git a/libs/settings/_locales/settings-jsdoc-strings.json b/libs/settings/_locales/settings-jsdoc-strings.json index 1f9fdccfe8f..8903537721a 100644 --- a/libs/settings/_locales/settings-jsdoc-strings.json +++ b/libs/settings/_locales/settings-jsdoc-strings.json @@ -5,11 +5,13 @@ "settings.list": "Return a list of settings starting with a given prefix.", "settings.programSecrets": "Program secrets", "settings.readBuffer": "Read named setting as a buffer. Returns undefined when setting not found.", + "settings.readJSON": "Read named setting as a JSON object.", "settings.readNumber": "Read named setting as a number.", "settings.readNumberArray": "Read named setting as a number.", "settings.readString": "Read named setting as a string.", "settings.remove": "Remove named setting.", "settings.writeBuffer": "Set named setting to a given buffer.", + "settings.writeJSON": "Set named settings to a given JSON object.", "settings.writeNumber": "Set named settings to a given number.", "settings.writeNumberArray": "Set named settings to a given array of numbers.", "settings.writeString": "Set named settings to a given string." diff --git a/libs/settings/_locales/settings-strings.json b/libs/settings/_locales/settings-strings.json index 3ae879c5b1d..a6cf7b8e815 100644 --- a/libs/settings/_locales/settings-strings.json +++ b/libs/settings/_locales/settings-strings.json @@ -2,5 +2,6 @@ "settings.deviceSecrets|block": "device secrets", "settings.programSecrets|block": "program secrets", "settings|block": "settings", + "{id:category}Config": "Config", "{id:category}Settings": "Settings" } \ No newline at end of file diff --git a/libs/settings/targetoverrides.ts b/libs/settings/targetoverrides.ts new file mode 100644 index 00000000000..5fd7136ac3a --- /dev/null +++ b/libs/settings/targetoverrides.ts @@ -0,0 +1,4 @@ +namespace config { + // this is the smallest setting possible on NRF + export const SETTINGS_SIZE_DEFL = (8*1024) +} diff --git a/package.json b/package.json index b4286630f84..2ba1f8d358a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pxt-microbit", - "version": "3.1.51", + "version": "9.1.1", "description": "micro:bit target for Microsoft MakeCode (PXT)", "keywords": [ "JavaScript", @@ -9,11 +9,11 @@ ], "repository": { "type": "git", - "url": "git+https://github.com/Microsoft/pxt-microbit.git" + "url": "git+https://github.com/microsoft/pxt-microbit.git" }, "author": "", "license": "MIT", - "homepage": "https://github.com/Microsoft/pxt-microbit#readme", + "homepage": "https://github.com/microsoft/pxt-microbit#readme", "files": [ "README.md", "pxtarget.json", @@ -32,19 +32,28 @@ "docs/static/icons/favicon.ico" ], "devDependencies": { - "@types/bluebird": "2.0.33", + "@types/dom-mediacapture-record": "1.0.20", "@types/marked": "0.3.0", - "@types/node": "8.0.53", - "@types/react": "16.0.25", + "@types/node": "8.10.66", + "@types/react": "16.4.7", "@types/react-dom": "16.0.3", "@types/web-bluetooth": "0.0.4", - "less": "2.7.3", + "less": "3.13.1", "react": "16.8.3", - "semantic-ui-less": "2.2.14", - "typescript": "^3.7.5" + "react-dom": "16.11.0", + "semantic-ui-less": "2.4.1", + "typescript": "4.8.3" }, "dependencies": { - "pxt-common-packages": "8.5.2", - "pxt-core": "6.9.9" + "pxt-common-packages": "14.0.2", + "pxt-core": "13.0.1" + }, + "overrides": { + "@blockly/field-grid-dropdown": { + "blockly": "13.1.1" + }, + "@blockly/plugin-workspace-search": { + "blockly": "13.1.1" + } } } diff --git a/pxtarget.json b/pxtarget.json index edab3b246d1..0e62fcf3236 100644 --- a/pxtarget.json +++ b/pxtarget.json @@ -7,13 +7,20 @@ "corepkg": "core", "bundleddirs": [ "libs/core", + "libs/audio-samples", "libs/radio", "libs/devices", "libs/bluetooth", "libs/servo", "libs/radio-broadcast", "libs/microphone", - "libs/settings" + "libs/settings", + "libs/flashlog", + "libs/datalogger", + "libs/bitmap", + "libs/fonts", + "libs/color", + "libs/audio-recording" ], "cloud": { "workspace": false, @@ -22,12 +29,43 @@ "thumbnails": true, "publishing": true, "importing": true, + "showBadges": false, "preferredPackages": [ "Microsoft/pxt-neopixel" ], "githubPackages": true, "cloudProviders": { - "github": {} + "github": { + "id": "github", + "name": "GitHub", + "icon": "/static/providers/github-mark.png", + "identity": false, + "order": 3 + }, + "microsoft": { + "id": "microsoft", + "name": "Microsoft", + "icon": "/static/providers/microsoft-logo.svg", + "identity": true, + "redirect": true, + "order": 1 + }, + "google": { + "id": "google", + "name": "Google", + "icon": "/static/providers/google-logo.svg", + "identity": true, + "redirect": true, + "order": 2 + }, + "clever": { + "id": "clever", + "name": "Clever", + "icon": "/static/providers/clever-logo.png", + "identity": true, + "redirect": true, + "order": 3 + } } }, "compile": { @@ -134,12 +172,12 @@ "useNewFunctions": true }, "compileService": { - "yottaTarget": "bbc-microbit-classic-gcc", + "yottaTarget": "bbc-microbit-classic-gcc@https://github.com/lancaster-university/yotta-target-bbc-microbit-classic-gcc", "yottaCorePackage": "microbit", "githubCorePackage": "lancaster-university/microbit", "gittag": "v2.2.0-rc6", "serviceId": "microbit", - "dockerImage": "pext/yotta:latest" + "dockerImage": "pext/yotta:gcc5" }, "multiVariants": [ "mbdal", @@ -153,20 +191,21 @@ }, "mbcodal": { "compile": { - "flashUsableEnd": null, - "flashEnd": null + "flashCodeAlign": 4096, + "flashUsableEnd": 471040, + "flashEnd": 524288 }, "compileService": { "buildEngine": "codal", "codalTarget": { "name": "codal-microbit-v2", "url": "https://github.com/lancaster-university/codal-microbit-v2", - "branch": "v0.2.24", + "branch": "v0.3.5", "type": "git" }, "codalBinary": "MICROBIT", "githubCorePackage": "lancaster-university/microbit-v2-samples", - "gittag": "v0.2.11", + "gittag": "v0.2.13", "serviceId": "mbcodal2", "dockerImage": "pext/yotta:latest", "yottaConfigCompatibility": true @@ -176,6 +215,10 @@ "runtime": { "mathBlocks": true, "loopsBlocks": true, + "pauseUntilBlock": { + "category": "loops", + "weight": 25 + }, "logicBlocks": true, "variablesBlocks": true, "textBlocks": true, @@ -211,11 +254,14 @@ "parts": true, "partsAspectRatio": 0.69, "messageSimulators": { - "jacdac": { - "url": "https://microsoft.github.io/jacdac-ts/tools/makecode-sim?webusb=0&parentOrigin=$PARENT_ORIGIN$", - "localHostUrl": "http://localhost:8000/tools/makecode-sim?webusb=0&parentOrigin=$PARENT_ORIGIN$" + "robot": { + "url": "https://microsoft.github.io/microbit-robot/?parentOrigin=$PARENT_ORIGIN$", + "localHostUrl": "http://localhost:3000/microbit-robot/?parentOrigin=$PARENT_ORIGIN$", + "aspectRatio": 1.22, + "permanent": true } }, + "testSimulatorExtensions": {}, "boardDefinition": { "visual": "microbit", "gpioPinBlocks": [ @@ -303,7 +349,10 @@ "builtinspeaker", "microphone", - "logotouch" + "logotouch", + "flashlog", + + "v2" ], "pinStyles": { "P0": "croc", @@ -328,7 +377,7 @@ "graphBackground": "#d9d9d9", "lineColors": [ "#6633cc", - "#3891A6", + "#2C7485", "#3454D1", "#EF767A", "#F46197", @@ -350,10 +399,16 @@ "appLogo": "./static/icons/apple-touch-icon.png", "organization": "Microsoft MakeCode", "organizationUrl": "https://makecode.com/", - "organizationLogo": "./static/Microsoft-logo_rgb_c-gray-square.png", - "organizationWideLogo": "./static/Microsoft-logo_rgb_c-white.png", - "homeScreenHero": "./static/hero.png", - "homeScreenHeroGallery": "/tutorials", + "organizationLogo": "./static/Microsoft_logo_rgb_W-white_D-square.png", + "organizationWideLogo": "./static/Microsoft_logo_rgb_W-white_D.png", + "homeScreenHero": { + "imageUrl": "/static/herogallery/hero-banner.png", + "name": "Flashing Heart", + "url": "/projects/flashing-heart", + "description": "New? Start here!", + "cardType": "tutorial" + }, + "homeScreenHeroGallery": "/hero-banner", "homeUrl": "https://makecode.microbit.org/", "embedUrl": "https://makecode.microbit.org/", "shareUrl": "https://makecode.microbit.org/", @@ -365,22 +420,26 @@ "driveDisplayName": "MICROBIT", "appStoreID": "1092687276", "mobileSafariDownloadProtocol": "microbithex://?data", - "crowdinProject": "kindscript", + "crowdinProject": "makecode", "extendEditor": true, "extendFieldEditors": true, "enableTrace": true, "ignoreDocsErrors": false, "errorList": true, + "workspaceSearch": true, "allowPackageExtensions": true, + "addNewTypeScriptFile": true, + "enabledFeatures": { + "blocksErrorList": {}, + "aiErrorHelp": {} + }, "experiments": [ - "autoWebUSBDownload", - "instructions", - "accessibleBlocks", "debugExtensionCode", "bluetoothUartConsole", - "bluetoothPartialFlashing", - "simScreenshot", - "simGif" + "bluetoothPartialFlashing" + ], + "supportedExperiences": [ + "code-eval" ], "bluetoothUartFilters": [ { @@ -402,73 +461,11 @@ }, { "name": "Buy", - "path": "https://microbit.org/resellers" + "path": "https://microbit.org/buy/" } ], "hasReferenceDocs": true, "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.jpg" - }, - { - "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" - } - ], "hideHomeDetailsVideo": true, "invertedMenu": true, "coloredToolbox": true, @@ -498,6 +495,8 @@ "functions": "#3455DB", "arrays": "#E65722" }, + "defaultColorTheme": "microbit-light", + "highContrastColorTheme": "pxt-high-contrast", "blocksCollapsing": true, "highContrast": true, "greenScreen": true, @@ -507,19 +506,23 @@ "en", "ar", "bg", + "ca", "cs", + "cy", "da", "de", "el", "es-ES", "fi", "fr", + "gn", "he", "hu", "is", "it", "ja", "ko", + "lo", "nl", "nb", "nn-NO", @@ -529,19 +532,31 @@ "ru", "si-LK", "sk", + "sr", "sv-SE", "tr", "uk", + "vi", "zh-CN", "zh-TW" ], "monacoColors": { "editor.background": "#ecf0f1" }, + "monacoFieldEditors": [ + "soundeffect-editor", + "image-editor" + ], "browserDbPrefixes": { "1": "v1", "2": "v2", - "3": "v3" + "3": "v3", + "4": "v4", + "5": "v5", + "6": "v6", + "7": "v7", + "8": "v8", + "9": "v9" }, "editorVersionPaths": { "0": "v0" @@ -551,7 +566,10 @@ "debugger": true, "simGifTransparent": "rgba(0,0,0,0)", "simGifMaxFrames": 44, + "simScreenshot": true, "simScreenshotMaxUriLength": 300000, + "simGif": true, + "simGifWidth": 240, "qrCode": true, "importExtensionFiles": true, "nameProjectFirst": true, @@ -559,7 +577,50 @@ "chooseLanguageRestrictionOnNewProject": true, "openProjectNewTab": true, "python": true, - "appFlashingTroubleshoot": "/device/windows-app/troubleshoot" + "appFlashingTroubleshoot": "/device/windows-app/troubleshoot", + "immersiveReader": true, + "tutorialCodeValidation": true, + "downloadDialogTheme": { + "webUSBDeviceNames": ["BBC micro:bit CMSIS-DAP", "DAPLink CMSIS-DAP"], + "minimumFirmwareVersion": "0249", + "deviceIcon": "xicon microbit", + "deviceSuccessIcon": "xicon microbit-check", + + "downloadMenuHelpURL" : "/device/usb", + "downloadHelpURL" : "/device/usb", + "troubleshootWebUSBHelpURL": "/device/usb/webusb/troubleshoot", + "incompatibleHardwareHelpURL": "/device/v2", + + "dragFileImage": "/static/download/transfer.png", + "connectDeviceImage": "/static/download/connect-microbit.gif", + "disconnectDeviceImage": "/static/download/full-reset.gif", + "selectDeviceImage": "/static/download/selecting-microbit.gif", + "connectionSuccessImage": "/static/download/successfully-paired.png", + "incompatibleHardwareImage": "/static/download/incompatible.png", + "usbDeviceForgottenImage": "/static/download/device-forgotten.gif", + "browserUnpairImage": "/static/download/browser-unpair-image.gif" + }, + "winAppDeprImage": "/static/winapp.PNG", + "showWinAppDeprBanner": false, + "tours": { + "editor": "/tours/editor-tour" + }, + "tutorialSimSidebarLangs": [ + "blocks", + "javascript", + "python" + ], + "preferWebUSBDownload": true, + "hideReplaceMyCode": true, + "matchWebUSBDeviceInSim": true, + "condenseProfile": true, + "cloudProfileIcon": "/static/profile/microbit-cloud.png", + "timeMachine": true, + "timeMachineDiffInterval": 600000, + "timeMachineSnapshotInterval": 1800000, + "shareHomepageContent": true, + "songEditor": true, + "blocklyKeyboardControlsByDefault": true }, "queryVariants": { "hidemenu": { @@ -567,6 +628,11 @@ "hideMenuBar": true } }, + "hidelanguage": { + "appTheme": { + "selectLanguage": false + } + }, "androidapp": { "compile": { "webUSB": false @@ -574,6 +640,18 @@ "appTheme": { "disableBlobObjectDownload": true } + }, + "skillsMap=1": { + "appTheme": { + "hideReplaceMyCode": false + } + }, + "teachertool=1": { + "appTheme": { + "hideMenuBar": true, + "workspaceSearch": true, + "noReloadOnUpdate": true + } } }, "uploadDocs": true diff --git a/resources/generateleds/generateleds.js b/resources/generateleds/generateleds.js index a043b5c414d..e567ba667ae 100644 --- a/resources/generateleds/generateleds.js +++ b/resources/generateleds/generateleds.js @@ -233,7 +233,7 @@ const icons = { . . # . . # # # . . # # # . .`, - eigthnote: ` + eighthnote: ` . . # . . . . # # . . . # . # diff --git a/sim/dalboard.ts b/sim/dalboard.ts index 4ff765839f3..c4af045ef57 100644 --- a/sim/dalboard.ts +++ b/sim/dalboard.ts @@ -8,7 +8,8 @@ namespace pxsim { , RadioBoard , LightBoard , MicrophoneBoard - , ControlMessageBoard { + , ControlMessageBoard + , samples.SampleBoard { // state & update logic for component services ledMatrixState: LedMatrixState; edgeConnectorState: EdgeConnectorState; @@ -19,12 +20,14 @@ namespace pxsim { lightSensorState: LightSensorState; buttonPairState: ButtonPairState; radioState: RadioState; - microphoneState: AnalogSensorState; + microphoneState: MicrophoneState; + recordingState: RecordingState; lightState: pxt.Map; fileSystem: FileSystemState; logoTouch: Button; speakerEnabled: boolean = true; controlMessageState: ControlMessageState; + samplesState: samples.SamplesState; // visual viewHost: visuals.BoardHost; @@ -97,7 +100,8 @@ namespace pxsim { ID_RADIO: DAL.MICROBIT_ID_RADIO, RADIO_EVT_DATAGRAM: DAL.MICROBIT_RADIO_EVT_DATAGRAM }); - this.builtinParts["microphone"] = this.microphoneState = new AnalogSensorState(DAL.DEVICE_ID_MICROPHONE, 0, 255, 86, 165); + this.builtinParts["microphone"] = this.microphoneState = new MicrophoneState(DAL.DEVICE_ID_MICROPHONE, 0, 255, 86, 165); + this.builtinParts["recording"] = this.recordingState = new RecordingState(); this.builtinParts["accelerometer"] = this.accelerometerState = new AccelerometerState(runtime); this.builtinParts["serial"] = this.serialState = new SerialState(runtime, this); this.builtinParts["thermometer"] = this.thermometerState = new ThermometerState(); @@ -117,6 +121,8 @@ namespace pxsim { this.builtinPartVisuals["buttonpair"] = (xy: visuals.Coord) => visuals.mkBtnSvg(xy); this.builtinPartVisuals["ledmatrix"] = (xy: visuals.Coord) => visuals.mkLedMatrixSvg(xy, 8, 8); this.builtinPartVisuals["microservo"] = (xy: visuals.Coord) => visuals.mkMicroServoPart(xy); + + this.samplesState = new samples.SamplesState(); } ensureHardwareVersion(version: number) { @@ -135,7 +141,13 @@ namespace pxsim { const cmpDefs = msg.partDefinitions || {}; const fnArgs = msg.fnArgs; - const v2Parts: pxt.Map = { "microphone": true, "logotouch": true, "builtinspeaker": true }; + const v2Parts: pxt.Map = { + "microphone": true, + "logotouch": true, + "builtinspeaker": true, + "flashlog": true, + "v2": true + }; if (msg.builtinParts) { const v2PartsUsed = msg.builtinParts.filter(k => v2Parts[k]) if (v2PartsUsed.length) { @@ -163,8 +175,16 @@ namespace pxsim { }), opts); document.body.innerHTML = ""; // clear children + if (shouldShowMute()) { + document.body.appendChild(createMuteButton()); + AudioContextManager.mute(true); + setParentMuteState("disabled"); + } document.body.appendChild(this.view = this.viewHost.getView()); + if (msg.theme === "mbcodal") { + this.ensureHardwareVersion(2); + } return Promise.resolve(); } @@ -184,6 +204,11 @@ namespace pxsim { screenshotAsync(width?: number): Promise { return this.viewHost.screenshotAsync(width); } + + kill() { + super.kill(); + this.viewHost.removeEventListeners(); + } } export function initRuntimeWithDalBoard() { diff --git a/sim/public/icons/jacdac.svg b/sim/public/icons/jacdac.svg new file mode 100644 index 00000000000..f56c4d37fe5 --- /dev/null +++ b/sim/public/icons/jacdac.svg @@ -0,0 +1 @@ + diff --git a/sim/public/sim.manifest b/sim/public/sim.manifest index 1b3c8232aef..8086bbc8f83 100644 --- a/sim/public/sim.manifest +++ b/sim/public/sim.manifest @@ -1,7 +1,6 @@ CACHE MANIFEST CACHE: -/cdn/bluebird.min.js /cdn/pxtsim.js /sim/sim.js diff --git a/sim/public/simulator.html b/sim/public/simulator.html index 391ba9812f1..6758a2bf237 100644 --- a/sim/public/simulator.html +++ b/sim/public/simulator.html @@ -18,24 +18,59 @@ overflow: hidden; margin: 0; } + +#safari-mute-button-outer { + position: absolute; + top: 10px; + right: 10px; +} +.safari-mute-button { + z-index: 1; + cursor: pointer; + position: relative; + display: inline-block; + min-height: 1rem; + outline: none; + border: 1px solid transparent; + vertical-align: middle; + margin: 0; + padding: 0.75rem; + text-transform: none; + text-shadow: none; + font-weight: 400; + line-height: 1em; + font-style: normal; + font-size: 16px; + text-align: center; + text-decoration: none; + border-radius: 0.2em; + user-select: none; + transition: opacity .1s ease,background-color .1s ease,box-shadow .1s ease,color .1s ease,background .1s ease; + -webkit-tap-highlight-color: transparent; + background-color: #e41b21; +} +.safari-mute-button:hover { + filter: grayscale(.15) brightness(.85) contrast(1.3); +} +.safari-mute-button:focus { + border: 1px solid white; + outline: 3px solid #4D90FE; +} +.safari-mute-button svg { + width: 24px; + height: 24px; +} +.safari-mute-button path { + fill: white; +} +@media screen and (max-height: 160px) { + .safari-mute-button { + display: none; + } +} - - diff --git a/sim/state/arcadeshield.ts b/sim/state/arcadeshield.ts new file mode 100644 index 00000000000..5ad47ad1e83 --- /dev/null +++ b/sim/state/arcadeshield.ts @@ -0,0 +1,23 @@ +namespace pxsim.pxtcore { + export function updateScreen(img: RefImage) { + + } + export function updateStats(s: string) { + + } + export function setPalette(b: RefBuffer) { + + } + export function setScreenBrightness(b: number) { + + } + export function displayHeight(): number { + return 120 + } + export function displayWidth(): number { + return 160 + } + export function displayPresent(): boolean { + return true + } +} diff --git a/sim/state/audio-samples.ts b/sim/state/audio-samples.ts new file mode 100644 index 00000000000..c72b676f0aa --- /dev/null +++ b/sim/state/audio-samples.ts @@ -0,0 +1,79 @@ +namespace pxsim.samples { + export interface SampleBoard extends EventBusBoard { + samplesState: SamplesState; + } + + class SampleChannel { + sampleRate = 11000; + protected playing: AudioContextManager.PlaySampleResult; + + constructor(public id: number) { + + } + + playSampleAsync(sample: RefBuffer) { + if (this.playing) { + this.playing.cancel(); + } + this.playing = AudioContextManager.startSamplePlayback(sample, BufferMethods.NumberFormat.UInt8LE, 255, this.sampleRate, music.volume() / 0xff); + this.playing.promise.then(() => { + this.playing = undefined; + }); + } + } + + export class SamplesState { + protected channels: SampleChannel[] = []; + protected enabled: boolean = false; + + constructor() { + this.channels = [ + new SampleChannel(0), + new SampleChannel(1), + new SampleChannel(2), + new SampleChannel(3), + ]; + } + + setEnabled(enabled: boolean): void { + this.enabled = enabled; + } + + setSampleRate(channelId: number, sampleRate: number): void { + channelId |= 0; + if (channelId < 0 || channelId >= this.channels.length) { + return; + } + + this.channels[channelId].sampleRate = sampleRate; + } + + playSampleAsync(channelId: number, sample: RefBuffer): void { + if (!this.enabled) { + return; + } + channelId |= 0; + if (channelId < 0 || channelId >= this.channels.length) { + return; + } + + this.channels[channelId].playSampleAsync(sample); + } + } + + export function enable(): void { + board().samplesState.setEnabled(true); + } + + export function disable(): void { + board().samplesState.setEnabled(false); + } + + export function setSampleRate(src: number, sampleRate: number): void { + board().samplesState.setSampleRate(src, sampleRate); + } + + export function playAsync(src: number, buf: RefBuffer): void { + board().samplesState.playSampleAsync(src, buf); + } +} \ No newline at end of file diff --git a/sim/state/bitmap.ts b/sim/state/bitmap.ts new file mode 100644 index 00000000000..feb188952bf --- /dev/null +++ b/sim/state/bitmap.ts @@ -0,0 +1,1110 @@ +namespace pxsim { + export class RefImage extends RefObject { + _width: number; + _height: number; + _bpp: number; + data: Uint8Array; + isStatic = true; + revision: number; + + constructor(w: number, h: number, bpp: number) { + super(); + this.revision = 0; + this.data = new Uint8Array(w * h) + this._width = w + this._height = h + this._bpp = bpp + } + + scan(mark: (path: string, v: any) => void) { } + gcKey() { return "Image" } + gcSize() { return 4 + (this.data.length + 3 >> 3) } + gcIsStatic() { return this.isStatic } + + pix(x: number, y: number) { + return (x | 0) + (y | 0) * this._width + } + + inRange(x: number, y: number) { + return 0 <= (x | 0) && (x | 0) < this._width && + 0 <= (y | 0) && (y | 0) < this._height; + } + + color(c: number): number { + return c & 0xff + } + + clamp(x: number, y: number) { + x |= 0 + y |= 0 + + if (x < 0) x = 0 + else if (x >= this._width) + x = this._width - 1 + + if (y < 0) y = 0 + else if (y >= this._height) + y = this._height - 1 + + return [x, y] + } + + makeWritable() { + this.revision++; + this.isStatic = false + } + + toDebugString() { + return this._width + "x" + this._height + } + } +} + +namespace pxsim.BitmapMethods { + export function XX(x: number) { return (x << 16) >> 16 } + export function YY(x: number) { return x >> 16 } + + export function __buffer(img: RefImage): RefBuffer { + return new RefBuffer(img.data) // no clone for now + } + + export function width(img: RefImage) { return img._width } + + export function height(img: RefImage) { return img._height } + + export function isMono(img: RefImage) { return img._bpp == 1 } + + export function isStatic(img: RefImage) { return img.gcIsStatic() } + + export function revision(img: RefImage) { return img.revision } + + export function setPixel(img: RefImage, x: number, y: number, c: number) { + img.makeWritable() + if (img.inRange(x, y)) + img.data[img.pix(x, y)] = img.color(c) + } + + export function getPixel(img: RefImage, x: number, y: number) { + if (img.inRange(x, y)) + return img.data[img.pix(x, y)] + return 0 + } + + export function fill(img: RefImage, c: number) { + img.makeWritable() + img.data.fill(img.color(c)) + } + + export function fillRect(img: RefImage, x: number, y: number, w: number, h: number, c: number) { + if (w == 0 || h == 0 || x >= img._width || y >= img._height || x + w - 1 < 0 || y + h - 1 < 0) + return; + img.makeWritable() + let [x2, y2] = img.clamp(x + w - 1, y + h - 1); + [x, y] = img.clamp(x, y) + let p = img.pix(x, y) + w = x2 - x + 1 + h = y2 - y + 1 + let d = img._width - w + c = img.color(c) + while (h-- > 0) { + for (let i = 0; i < w; ++i) + img.data[p++] = c + p += d + } + } + + export function _fillRect(img: RefImage, xy: number, wh: number, c: number) { + fillRect(img, XX(xy), YY(xy), XX(wh), YY(wh), c) + } + + export function mapRect(img: RefImage, x: number, y: number, w: number, h: number, c: RefBuffer) { + if (c.data.length < 16) + return + img.makeWritable() + let [x2, y2] = img.clamp(x + w - 1, y + h - 1); + [x, y] = img.clamp(x, y) + let p = img.pix(x, y) + w = x2 - x + 1 + h = y2 - y + 1 + let d = img._width - w + + while (h-- > 0) { + for (let i = 0; i < w; ++i) { + img.data[p] = c.data[img.data[p]] + p++ + } + p += d + } + } + + export function _mapRect(img: RefImage, xy: number, wh: number, c: RefBuffer) { + mapRect(img, XX(xy), YY(xy), XX(wh), YY(wh), c) + } + + export function equals(img: RefImage, other: RefImage) { + if (!other || img._bpp != other._bpp || img._width != other._width || img._height != other._height) { + return false; + } + let imgData = img.data; + let otherData = other.data; + let len = imgData.length; + for (let i = 0; i < len; i++) { + if (imgData[i] != otherData[i]) { + return false; + } + } + return true; + } + + export function getRows(img: RefImage, x: number, dst: RefBuffer) { + x |= 0 + if (!img.inRange(x, 0)) + return + + let dp = 0 + let len = Math.min(dst.data.length, (img._width - x) * img._height) + let sp = x + let hh = 0 + while (len--) { + if (hh++ >= img._height) { + hh = 1 + sp = ++x + } + dst.data[dp++] = img.data[sp] + sp += img._width + } + } + + export function setRows(img: RefImage, x: number, src: RefBuffer) { + x |= 0 + if (!img.inRange(x, 0)) + return + + let sp = 0 + let len = Math.min(src.data.length, (img._width - x) * img._height) + let dp = x + let hh = 0 + while (len--) { + if (hh++ >= img._height) { + hh = 1 + dp = ++x + } + img.data[dp] = src.data[sp++] + dp += img._width + } + } + + export function clone(img: RefImage) { + let r = new RefImage(img._width, img._height, img._bpp) + r.data.set(img.data) + return r + } + + export function flipX(img: RefImage) { + img.makeWritable() + const w = img._width + const h = img._height + for (let i = 0; i < h; ++i) { + img.data.subarray(i * w, (i + 1) * w).reverse() + } + } + + + export function flipY(img: RefImage) { + img.makeWritable() + const w = img._width + const h = img._height + const d = img.data + for (let i = 0; i < w; ++i) { + let top = i + let bot = i + (h - 1) * w + while (top < bot) { + let c = d[top] + d[top] = d[bot] + d[bot] = c + top += w + bot -= w + } + } + } + + export function transposed(img: RefImage) { + const w = img._width + const h = img._height + const d = img.data + const r = new RefImage(h, w, img._bpp) + const n = r.data + let src = 0 + + for (let i = 0; i < h; ++i) { + let dst = i + for (let j = 0; j < w; ++j) { + n[dst] = d[src++] + dst += w + } + } + + return r + } + + export function copyFrom(img: RefImage, from: RefImage) { + if (img._width != from._width || img._height != from._height || + img._bpp != from._bpp) + return; + img.data.set(from.data) + } + + export function scroll(img: RefImage, dx: number, dy: number) { + img.makeWritable() + dx |= 0 + dy |= 0 + if (dx != 0) { + const img2 = clone(img) + img.data.fill(0) + drawTransparentBitmap(img, img2, dx, dy) + } else if (dy < 0) { + dy = -dy + if (dy < img._height) + img.data.copyWithin(0, dy * img._width) + else + dy = img._height + img.data.fill(0, (img._height - dy) * img._width) + } else if (dy > 0) { + if (dy < img._height) + img.data.copyWithin(dy * img._width, 0) + else + dy = img._height + img.data.fill(0, 0, dy * img._width) + } + // TODO implement dx + } + + export function replace(img: RefImage, from: number, to: number) { + to &= 0xf; + const d = img.data + for (let i = 0; i < d.length; ++i) + if (d[i] == from) d[i] = to + } + + export function doubledX(img: RefImage) { + const w = img._width + const h = img._height + const d = img.data + const r = new RefImage(w * 2, h, img._bpp) + const n = r.data + let dst = 0 + + for (let src = 0; src < d.length; ++src) { + let c = d[src] + n[dst++] = c + n[dst++] = c + } + + return r + } + + export function doubledY(img: RefImage) { + const w = img._width + const h = img._height + const d = img.data + const r = new RefImage(w, h * 2, img._bpp) + const n = r.data + + let src = 0 + let dst0 = 0 + let dst1 = w + for (let i = 0; i < h; ++i) { + for (let j = 0; j < w; ++j) { + let c = d[src++] + n[dst0++] = c + n[dst1++] = c + } + dst0 += w + dst1 += w + } + + return r + } + + + export function doubled(img: RefImage) { + return doubledX(doubledY(img)) + } + + function drawImageCore(img: RefImage, from: RefImage, x: number, y: number, clear: boolean, check: boolean) { + x |= 0 + y |= 0 + + const w = from._width + let h = from._height + const sh = img._height + const sw = img._width + + if (x + w <= 0) return false + if (x >= sw) return false + if (y + h <= 0) return false + if (y >= sh) return false + + if (clear) + fillRect(img, x, y, from._width, from._height, 0) + else if (!check) + img.makeWritable() + + const len = x < 0 ? Math.min(sw, w + x) : Math.min(sw - x, w) + const fdata = from.data + const tdata = img.data + + for (let p = 0; h--; y++ , p += w) { + if (0 <= y && y < sh) { + let dst = y * sw + let src = p + if (x < 0) + src += -x + else + dst += x + for (let i = 0; i < len; ++i) { + const v = fdata[src++] + if (v) { + if (check) { + if (tdata[dst]) + return true + } else { + tdata[dst] = v + } + } + dst++ + } + } + } + + return false + } + + export function drawBitmap(img: RefImage, from: RefImage, x: number, y: number) { + drawImageCore(img, from, x, y, true, false) + } + + export function drawTransparentBitmap(img: RefImage, from: RefImage, x: number, y: number) { + drawImageCore(img, from, x, y, false, false) + } + + export function overlapsWith(img: RefImage, other: RefImage, x: number, y: number) { + return drawImageCore(img, other, x, y, false, true) + } + + function drawLineLow(img: RefImage, x0: number, y0: number, x1: number, y1: number, c: number) { + let dx = x1 - x0; + let dy = y1 - y0; + let yi = img._width; + if (dy < 0) { + yi = -yi; + dy = -dy; + } + let D = 2 * dy - dx; + dx <<= 1; + dy <<= 1; + c = img.color(c); + let ptr = img.pix(x0, y0) + for (let x = x0; x <= x1; ++x) { + img.data[ptr] = c + if (D > 0) { + ptr += yi; + D -= dx; + } + D += dy; + ptr++; + } + } + + function drawLineHigh(img: RefImage, x0: number, y0: number, x1: number, y1: number, c: number) { + let dx = x1 - x0; + let dy = y1 - y0; + let xi = 1; + if (dx < 0) { + xi = -1; + dx = -dx; + } + let D = 2 * dx - dy; + dx <<= 1; + dy <<= 1; + c = img.color(c); + let ptr = img.pix(x0, y0); + for (let y = y0; y <= y1; ++y) { + img.data[ptr] = c; + if (D > 0) { + ptr += xi; + D -= dy; + } + D += dx; + ptr += img._width; + } + } + + export function _drawLine(img: RefImage, xy: number, wh: number, c: number) { + drawLine(img, XX(xy), YY(xy), XX(wh), YY(wh), c) + } + + export function drawLine(img: RefImage, x0: number, y0: number, x1: number, y1: number, c: number) { + x0 |= 0 + y0 |= 0 + x1 |= 0 + y1 |= 0 + + if (x1 < x0) { + drawLine(img, x1, y1, x0, y0, c); + return; + } + + let w = x1 - x0; + let h = y1 - y0; + + if (h == 0) { + if (w == 0) + setPixel(img, x0, y0, c); + else + fillRect(img, x0, y0, w + 1, 1, c); + return; + } + + if (w == 0) { + if (h > 0) + fillRect(img, x0, y0, 1, h + 1, c); + else + fillRect(img, x0, y1, 1, -h + 1, c); + return; + } + + if (x1 < 0 || x0 >= img._width) + return; + if (x0 < 0) { + y0 -= (h * x0 / w) | 0; + x0 = 0; + } + if (x1 >= img._width) { + let d = (img._width - 1) - x1; + y1 += (h * d / w) | 0; + x1 = img._width - 1 + } + + if (y0 < y1) { + if (y0 >= img._height || y1 < 0) + return; + if (y0 < 0) { + x0 -= (w * y0 / h) | 0; + y0 = 0; + } + if (y1 >= img._height) { + let d = (img._height - 1) - y1; + x1 += (w * d / h) | 0; + y1 = img._height + } + } else { + if (y1 >= img._height || y0 < 0) + return; + if (y1 < 0) { + x1 -= (w * y1 / h) | 0; + y1 = 0; + } + if (y0 >= img._height) { + let d = (img._height - 1) - y0; + x0 += (w * d / h) | 0; + y0 = img._height + } + } + + img.makeWritable() + + if (h < 0) { + h = -h; + if (h < w) + drawLineLow(img, x0, y0, x1, y1, c); + else + drawLineHigh(img, x1, y1, x0, y0, c); + } else { + if (h < w) + drawLineLow(img, x0, y0, x1, y1, c); + else + drawLineHigh(img, x0, y0, x1, y1, c); + } + } + + export function drawIcon(img: RefImage, icon: RefBuffer, x: number, y: number, color: number) { + const src: Uint8Array = icon.data + if (!bitmaps.isValidImage(icon)) + return + if (src[1] != 1) + return // only mono + let width = bitmaps.bufW(src) + let height = bitmaps.bufH(src) + let byteH = bitmaps.byteHeight(height, 1) + + x |= 0 + y |= 0 + const destHeight = img._height + const destWidth = img._width + + if (x + width <= 0) return + if (x >= destWidth) return + if (y + height <= 0) return + if (y >= destHeight) return + + img.makeWritable() + + let srcPointer = 8 + color = img.color(color) + const screen = img.data + + for (let i = 0; i < width; ++i) { + let destX = x + i + if (0 <= destX && destX < destWidth) { + let destIndex = destX + y * destWidth + let srcIndex = srcPointer + let destY = y + let destEnd = Math.min(destHeight, height + y) + if (y < 0) { + srcIndex += ((-y) >> 3) + destY += ((-y) >> 3) * 8 + destIndex += (destY - y) * destWidth + } + let mask = 0x01 + let srcByte = src[srcIndex++] + while (destY < destEnd) { + if (destY >= 0 && (srcByte & mask)) { + screen[destIndex] = color + } + mask <<= 1 + if (mask == 0x100) { + mask = 0x01 + srcByte = src[srcIndex++] + } + destIndex += destWidth + destY++ + } + } + srcPointer += byteH + } + } + + export function _drawIcon(img: RefImage, icon: RefBuffer, xy: number, color: number) { + drawIcon(img, icon, XX(xy), YY(xy), color) + } + + export function fillCircle(img: RefImage, cx: number, cy: number, r: number, c: number) { + let x = r - 1; + let y = 0; + let dx = 1; + let dy = 1; + let err = dx - (r << 1); + while (x >= y) { + fillRect(img, cx + x, cy - y, 1, 1 + (y << 1), c); + fillRect(img, cx + y, cy - x, 1, 1 + (x << 1), c); + fillRect(img, cx - x, cy - y, 1, 1 + (y << 1), c); + fillRect(img, cx - y, cy - x, 1, 1 + (x << 1), c); + if (err <= 0) { + y++; + err += dy; + dy += 2; + } + if (err > 0) { + x--; + dx += 2; + err += dx - (r << 1); + } + } + } + + export function _fillCircle(img: RefImage, cxy: number, r: number, c: number) { + fillCircle(img, XX(cxy), YY(cxy), r, c); + } + + interface LineGenState { + x: number; + y: number; + x0: number; + y0: number; + x1: number; + y1: number; + W: number; + H: number; + dx: number; + dy: number; + yi: number; + xi: number; + D: number; + nextFuncIndex: number; + } + interface ValueRange { + min: number; + max: number; + } + + function nextYRange_Low(x: number, line: LineGenState, yRange: ValueRange) { + while (line.x === x && line.x <= line.x1 && line.x < line.W) { + if (0 <= line.x) { + if (line.y < yRange.min) yRange.min = line.y; + if (line.y > yRange.max) yRange.max = line.y + } + if (line.D > 0) { + line.y += line.yi; + line.D -= line.dx; + } + line.D += line.dy; + ++line.x; + } + } + + function nextYRange_HighUp(x: number, line: LineGenState, yRange: ValueRange) { + while (line.x == x && line.y >= line.y1 && line.x < line.W) { + if (0 <= line.x) { + if (line.y < yRange.min) yRange.min = line.y; + if (line.y > yRange.max) yRange.max = line.y; + } + if (line.D > 0) { + line.x += line.xi; + line.D += line.dy; + } + line.D += line.dx; + --line.y; + } + } + + function nextYRange_HighDown(x: number, line: LineGenState, yRange: ValueRange) { + while (line.x == x && line.y <= line.y1 && line.x < line.W) { + if (0 <= line.x) { + if (line.y < yRange.min) yRange.min = line.y; + if (line.y > yRange.max) yRange.max = line.y; + } + if (line.D > 0) { + line.x += line.xi; + line.D -= line.dy; + } + line.D += line.dx; + ++line.y; + } + } + + function initYRangeGenerator(X0: number, Y0: number, X1: number, Y1: number): LineGenState { + const line: LineGenState = { + x: X0, + y: Y0, + x0: X0, + y0: Y0, + x1: X1, + y1: Y1, + W: 0, + H: 0, + dx: X1 - X0, + dy: Y1 - Y0, + yi: 0, + xi: 0, + D: 0, + nextFuncIndex: 0, + }; + + if ((line.dy < 0 ? -line.dy : line.dy) < line.dx) { + line.yi = 1; + if (line.dy < 0) { + line.yi = -1; + line.dy = -line.dy; + } + line.D = 2 * line.dy - line.dx; + line.dx = line.dx << 1; + line.dy = line.dy << 1; + + line.nextFuncIndex = 0; + return line; + } else { + line.xi = 1; + if (line.dy < 0) { + line.D = 2 * line.dx + line.dy; + line.dx = line.dx << 1; + line.dy = line.dy << 1; + + line.nextFuncIndex = 1; + return line; + } else { + line.D = 2 * line.dx - line.dy; + line.dx = line.dx << 1; + line.dy = line.dy << 1; + + line.nextFuncIndex = 2; + return line; + } + } + } + + export function fillTriangle(img: RefImage, x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, c: number) { + if (x1 < x0) { + [x1, x0] = [x0, x1]; + [y1, y0] = [y0, y1]; + } + if (x2 < x1) { + [x2, x1] = [x1, x2]; + [y2, y1] = [y1, y2]; + } + if (x1 < x0) { + [x1, x0] = [x0, x1]; + [y1, y0] = [y0, y1]; + } + + const lines: LineGenState[] = [ + initYRangeGenerator(x0, y0, x2, y2), + initYRangeGenerator(x0, y0, x1, y1), + initYRangeGenerator(x1, y1, x2, y2) + ]; + + lines[0].W = lines[1].W = lines[2].W = width(img); + lines[0].H = lines[1].H = lines[2].H = height(img); + + type FP_NEXT = (x: number, line: LineGenState, yRange: ValueRange) => void; + const nextFuncList: FP_NEXT[] = [ + nextYRange_Low, + nextYRange_HighUp, + nextYRange_HighDown + ]; + const fpNext0 = nextFuncList[lines[0].nextFuncIndex]; + const fpNext1 = nextFuncList[lines[1].nextFuncIndex]; + const fpNext2 = nextFuncList[lines[2].nextFuncIndex]; + + const yRange= { + min: lines[0].H, + max: -1 + }; + + for (let x = lines[1].x0; x <= lines[1].x1; x++) { + yRange.min = lines[0].H; yRange.max = -1; + fpNext0(x, lines[0], yRange); + fpNext1(x, lines[1], yRange); + fillRect(img, x, yRange.min, 1, yRange.max - yRange.min + 1, c); + } + + fpNext2(lines[2].x0, lines[2], yRange); + + for (let x = lines[2].x0 + 1; x <= lines[2].x1; x++) { + yRange.min = lines[0].H; yRange.max = -1; + fpNext0(x, lines[0], yRange); + fpNext2(x, lines[2], yRange); + fillRect(img, x, yRange.min, 1, yRange.max - yRange.min + 1, c); + } + } + + export function _fillTriangle(img: RefImage, args: RefCollection) { + fillTriangle( + img, + args.getAt(0) | 0, + args.getAt(1) | 0, + args.getAt(2) | 0, + args.getAt(3) | 0, + args.getAt(4) | 0, + args.getAt(5) | 0, + args.getAt(6) | 0, + ); + } + + export function fillPolygon4(img: RefImage, x0: number, y0: number, x1: number, y1: number, x2: number, y2: number, x3: number, y3: number, c: number) { + const lines: LineGenState[]= [ + (x0 < x1) ? initYRangeGenerator(x0, y0, x1, y1) : initYRangeGenerator(x1, y1, x0, y0), + (x1 < x2) ? initYRangeGenerator(x1, y1, x2, y2) : initYRangeGenerator(x2, y2, x1, y1), + (x2 < x3) ? initYRangeGenerator(x2, y2, x3, y3) : initYRangeGenerator(x3, y3, x2, y2), + (x0 < x3) ? initYRangeGenerator(x0, y0, x3, y3) : initYRangeGenerator(x3, y3, x0, y0) + ]; + + lines[0].W = lines[1].W = lines[2].W = lines[3].W = width(img); + lines[0].H = lines[1].H = lines[2].H = lines[3].H = height(img); + + let minX = Math.min(Math.min(x0, x1), Math.min(x2, x3)); + let maxX = Math.min(Math.max(Math.max(x0, x1), Math.max(x2, x3)), lines[0].W - 1); + + type FP_NEXT = (x: number, line: LineGenState, yRange: ValueRange) => void; + const nextFuncList: FP_NEXT[] = [ + nextYRange_Low, + nextYRange_HighUp, + nextYRange_HighDown + ]; + + const fpNext0 = nextFuncList[lines[0].nextFuncIndex]; + const fpNext1 = nextFuncList[lines[1].nextFuncIndex]; + const fpNext2 = nextFuncList[lines[2].nextFuncIndex]; + const fpNext3 = nextFuncList[lines[3].nextFuncIndex]; + + const yRange: ValueRange = { + min: lines[0].H, + max: -1 + }; + + for (let x = minX; x <= maxX; x++) { + yRange.min = lines[0].H; yRange.max = -1; + fpNext0(x, lines[0], yRange); + fpNext1(x, lines[1], yRange); + fpNext2(x, lines[2], yRange); + fpNext3(x, lines[3], yRange); + fillRect(img, x,yRange.min, 1, yRange.max - yRange.min + 1, c); + } + } + + export function _fillPolygon4(img: RefImage, args: RefCollection) { + fillPolygon4( + img, + args.getAt(0) | 0, + args.getAt(1) | 0, + args.getAt(2) | 0, + args.getAt(3) | 0, + args.getAt(4) | 0, + args.getAt(5) | 0, + args.getAt(6) | 0, + args.getAt(7) | 0, + args.getAt(8) | 0, + ); + } + + export function _blitRow(img: RefImage, xy: number, from: RefImage, xh: number) { + blitRow(img, XX(xy), YY(xy), from, XX(xh), YY(xh)) + } + + export function blitRow(img: RefImage, x: number, y: number, from: RefImage, fromX: number, fromH: number) { + x |= 0 + y |= 0 + fromX |= 0 + fromH |= 0 + if (!img.inRange(x, 0) || !img.inRange(fromX, 0) || fromH <= 0) + return + let fy = 0 + let stepFY = ((from._width << 16) / fromH) | 0 + let endY = y + fromH + if (endY > img._height) + endY = img._height + if (y < 0) { + fy += -y * stepFY + y = 0 + } + while (y < endY) { + img.data[img.pix(x, y)] = from.data[from.pix(fromX, fy >> 16)] + y++ + fy += stepFY + } + } + + export function _blit(img: RefImage, src: RefImage, args: RefCollection): boolean { + return blit(img, src, args); + } + + export function blit(dst: RefImage, src: RefImage, args: RefCollection): boolean { + const xDst = args.getAt(0) as number; + const yDst = args.getAt(1) as number; + const wDst = args.getAt(2) as number; + const hDst = args.getAt(3) as number; + const xSrc = args.getAt(4) as number; + const ySrc = args.getAt(5) as number; + const wSrc = args.getAt(6) as number; + const hSrc = args.getAt(7) as number; + const transparent = args.getAt(8) as number; + const check = args.getAt(9) as number; + + const xSrcStep = ((wSrc << 16) / wDst) | 0; + const ySrcStep = ((hSrc << 16) / hDst) | 0; + + const xDstClip = Math.abs(Math.min(0, xDst)); + const yDstClip = Math.abs(Math.min(0, yDst)); + const xDstStart = xDst + xDstClip; + const yDstStart = yDst + yDstClip; + const xDstEnd = Math.min(dst._width, xDst + wDst); + const yDstEnd = Math.min(dst._height, yDst + hDst); + + const xSrcStart = Math.max(0, (xSrc << 16) + xDstClip * xSrcStep); + const ySrcStart = Math.max(0, (ySrc << 16) + yDstClip * ySrcStep); + const xSrcEnd = Math.min(src._width, xSrc + wSrc) << 16; + const ySrcEnd = Math.min(src._height, ySrc + hSrc) << 16; + + if (!check) + dst.makeWritable(); + + for (let yDstCur = yDstStart, ySrcCur = ySrcStart; yDstCur < yDstEnd && ySrcCur < ySrcEnd; ++yDstCur, ySrcCur += ySrcStep) { + const ySrcCurI = ySrcCur >> 16; + for (let xDstCur = xDstStart, xSrcCur = xSrcStart; xDstCur < xDstEnd && xSrcCur < xSrcEnd; ++xDstCur, xSrcCur += xSrcStep) { + const xSrcCurI = xSrcCur >> 16; + const cSrc = getPixel(src, xSrcCurI, ySrcCurI); + if (check && cSrc) { + const cDst = getPixel(dst, xDstCur, yDstCur); + if (cDst) { + return true; + } + continue; + } + if (!transparent || cSrc) { + setPixel(dst, xDstCur, yDstCur, cSrc); + } + } + } + return false; + } +} + + +namespace pxsim.bitmaps { + export function byteHeight(h: number, bpp: number) { + if (bpp == 1) + return h * bpp + 7 >> 3 + else + return ((h * bpp + 31) >> 5) << 2 + } + + function isLegacyImage(buf: RefBuffer) { + if (!buf || buf.data.length < 5) + return false; + + if (buf.data[0] != 0xe1 && buf.data[0] != 0xe4) + return false; + + const bpp = buf.data[0] & 0xf; + const sz = buf.data[1] * byteHeight(buf.data[2], bpp) + if (4 + sz != buf.data.length) + return false; + + return true; + } + + export function bufW(data: Uint8Array) { + return data[2] | (data[3] << 8) + } + + export function bufH(data: Uint8Array) { + return data[4] | (data[5] << 8) + } + + export function isValidImage(buf: RefBuffer) { + if (!buf || buf.data.length < 5) + return false; + + if (buf.data[0] != 0x87) + return false + + if (buf.data[1] != 1 && buf.data[1] != 4) + return false; + + const bpp = buf.data[1]; + const sz = bufW(buf.data) * byteHeight(bufH(buf.data), bpp) + if (8 + sz != buf.data.length) + return false; + + return true; + } + + + export function create(w: number, h: number) { + // truncate decimal sizes + w |= 0 + h |= 0 + return new RefImage(w, h, 4) + } + + export function ofBuffer(buf: RefBuffer): RefImage { + const src: Uint8Array = buf.data + + let srcP = 4 + let w = 0, h = 0, bpp = 0 + + if (isLegacyImage(buf)) { + w = src[1] + h = src[2] + bpp = src[0] & 0xf; + // console.log("using legacy image") + } else if (isValidImage(buf)) { + srcP = 8 + w = bufW(src) + h = bufH(src) + bpp = src[1] + } + + if (w == 0 || h == 0) + return null + const r = new RefImage(w, h, bpp) + const dst = r.data + + r.isStatic = buf.isStatic + + if (bpp == 1) { + for (let i = 0; i < w; ++i) { + let dstP = i + let mask = 0x01 + let v = src[srcP++] + for (let j = 0; j < h; ++j) { + if (mask == 0x100) { + mask = 0x01 + v = src[srcP++] + } + if (v & mask) + dst[dstP] = 1 + dstP += w + mask <<= 1 + } + } + } else if (bpp == 4) { + for (let i = 0; i < w; ++i) { + let dstP = i + for (let j = 0; j < h >> 1; ++j) { + const v = src[srcP++] + dst[dstP] = v & 0xf + dstP += w + dst[dstP] = v >> 4 + dstP += w + } + if (h & 1) + dst[dstP] = src[srcP++] & 0xf + srcP = (srcP + 3) & ~3 + } + } + + return r + } + + export function toBuffer(img: RefImage): RefBuffer { + let col = byteHeight(img._height, img._bpp) + let sz = 8 + img._width * col + let r = new Uint8Array(sz) + r[0] = 0x87 + r[1] = img._bpp + r[2] = img._width & 0xff + r[3] = img._width >> 8 + r[4] = img._height & 0xff + r[5] = img._height >> 8 + let dstP = 8 + const w = img._width + const h = img._height + const data = img.data + for (let i = 0; i < w; ++i) { + if (img._bpp == 4) { + let p = i + for (let j = 0; j < h; j += 2) { + r[dstP++] = ((data[p + w] & 0xf) << 4) | ((data[p] || 0) & 0xf) + p += 2 * w + } + dstP = (dstP + 3) & ~3 + } else if (img._bpp == 1) { + let mask = 0x01 + let p = i + for (let j = 0; j < h; j++) { + if (data[p]) + r[dstP] |= mask + mask <<= 1 + p += w + if (mask == 0x100) { + mask = 0x01 + dstP++ + } + } + if (mask != 0x01) + dstP++ + } + } + + return new RefBuffer(r) + } + + export function doubledIcon(buf: RefBuffer): RefBuffer { + let img = ofBuffer(buf) + if (!img) + return null + img = BitmapMethods.doubled(img) + return toBuffer(img) + } +} diff --git a/sim/state/edgeconnector.ts b/sim/state/edgeconnector.ts index 5d40de90a7c..a81dc49f22f 100644 --- a/sim/state/edgeconnector.ts +++ b/sim/state/edgeconnector.ts @@ -29,6 +29,8 @@ namespace pxsim { } namespace pxsim.pins { + export let edgeConnectorSoundDisabled = false; + export function digitalReadPin(pinId: number): number { let pin = getPin(pinId); if (!pin) return -1; @@ -125,12 +127,12 @@ namespace pxsim.pins { export function analogPitch(frequency: number, ms: number) { // update analog output const b = board(); - if (!b) return; + if (!b || isNaN(frequency) || isNaN(ms)) return; const ec = b.edgeConnectorState; const pins = ec.pins; const pin = ec.pitchEnabled && (pins.filter(pin => !!pin && pin.pitch)[0] || pins[0]); const pitchVolume = ec.pitchVolume | 0; - if (pin) { + if (pin && !edgeConnectorSoundDisabled) { pin.mode = PinFlags.Analog | PinFlags.Output; if (frequency <= 0 || pitchVolume <= 0) { pin.value = 0; @@ -152,7 +154,7 @@ namespace pxsim.pins { else { setTimeout(() => { AudioContextManager.stop(); - if (pin) { + if (pin && !edgeConnectorSoundDisabled) { pin.value = 0; pin.period = 0; pin.mode = PinFlags.Unused; @@ -183,4 +185,41 @@ namespace pxsim.pins { export function setAudioPin(pinId: number) { pxsim.pins.analogSetPitchPin(pinId); } + + const disabledSVG = ` + + + + + + + + ` + + export function setAudioPinEnabled(enabled: boolean) { + edgeConnectorSoundDisabled = !enabled; + + const headphone = board().viewHost.getView().querySelector("g.sim-headphone-cmp"); + + if (headphone) { + const existing = headphone.querySelector("#headphone-disabled"); + + if (existing) { + if (enabled) { + existing.remove(); + } + else { + return; + } + } + + if (!enabled) { + const img = document.createElementNS("http://www.w3.org/2000/svg", "image") as SVGImageElement; + img.setAttribute("href", "data:image/svg+xml;utf8," + encodeURIComponent(disabledSVG)) + img.setAttribute("id", "headphone-disabled"); + img.style.transform = "scale(1.5) translate(-10px, -10px)"; + headphone.appendChild(img) + } + } + } } \ No newline at end of file diff --git a/sim/state/edgeconnectorsim.ts b/sim/state/edgeconnectorsim.ts index 4d0264d5f08..5c6c7e0a542 100644 --- a/sim/state/edgeconnectorsim.ts +++ b/sim/state/edgeconnectorsim.ts @@ -24,7 +24,7 @@ namespace pxsim { return this.value > 100 ? 1 : 0; } - digitalWritePin(value: number) { + digitalWritePin(value: number) { this.mode = PinFlags.Digital | PinFlags.Output; this.value = value > 0 ? 200 : 0; runtime.queueDisplayUpdate(); @@ -37,6 +37,11 @@ namespace pxsim { case PinPullMode.PullUp: this.value = 1023; break; default: this.value = Math_.randomRange(0, 1023); break; } + + // stop continuous servo if moving; 90 degrees represents a speed of 0 + if (this.servoContinuous && pull == PinPullMode.PullNone && this.mode & PinFlags.Digital) { + this.servoAngle = 90; + } } analogReadPin(): number { diff --git a/sim/state/flashlog.ts b/sim/state/flashlog.ts new file mode 100644 index 00000000000..f1cdd914a6d --- /dev/null +++ b/sim/state/flashlog.ts @@ -0,0 +1,173 @@ +namespace pxsim.flashlog { + enum FlashLogTimeStampFormat { + None = 0, + Milliseconds = 1, + Seconds = 10, + Minutes = 600, + Hours = 36000, + Days = 864000, + } + // we don't store the flash log in the runtime object, since it's persistent + let headers: string[] = [] + let currentRow: string[] = undefined + let SEPARATOR = "," + let timestampFormat: FlashLogTimeStampFormat = undefined + let mirrorToSerial = false; + let logSize = 0; + let committedCols = 0; + /** allocated flash size **/ + const logEnd = 121852; + + let lastRunId: string; + function init() { + const b = board(); + if (!b) return; + if (b.runOptions.id !== lastRunId) { + lastRunId = b.runOptions.id; + erase(); + } + b.ensureHardwareVersion(2); + } + + function commitRow(data: string, type: "headers" | "row" | "plaintext") { + if (!runtime) return; + data += "\n"; + + /** edge 18 does not support text encoder, so fall back to length **/ + logSize += typeof TextEncoder !== "undefined" ? (new TextEncoder().encode(data)).length : data.length; + if (logSize >= logEnd) { + board().bus.queue(DAL.MICROBIT_ID_LOG, DAL.MICROBIT_LOG_EVT_LOG_FULL); + clear(false); + } + if (mirrorToSerial) { + board().serialState.writeSerial(data); + } + + if (type !== "plaintext") { + board().serialState.writeCsv(data, type); + } + + } + + export function beginRow(): number { + init() + if (currentRow) + return DAL.DEVICE_INVALID_STATE + currentRow = [] + return DAL.DEVICE_OK + } + + export function logData(key: string, value: string, prepend = false) { + init() + if (!currentRow) + return DAL.DEVICE_INVALID_STATE + + // find header index + let index = headers.indexOf(key) + if (index < 0) { + if (prepend) { + /** push timestamps up to front of uncommitted rows **/ + headers.splice(committedCols, 0, key); + currentRow.splice(committedCols, 0, value); + index = committedCols; + } else { + headers.push(key) + index = headers.length - 1 + } + } + + // store + currentRow[index] = value + + return DAL.DEVICE_OK + } + + export function endRow(): number { + init() + if (!currentRow) + return DAL.DEVICE_INVALID_STATE + if (!currentRow.some(el => el !== "" && el != undefined)) + return DAL.DEVICE_OK; + + if (timestampFormat !== FlashLogTimeStampFormat.None) { + let unit = ""; + switch(timestampFormat) { + case FlashLogTimeStampFormat.Milliseconds: + unit = "milliseconds" + break; + case FlashLogTimeStampFormat.Minutes: + unit = "minutes"; + break; + case FlashLogTimeStampFormat.Hours: + unit = "hours"; + break; + case FlashLogTimeStampFormat.Days: + unit = "days"; + break; + case FlashLogTimeStampFormat.Seconds: + default: + unit = "seconds"; + break; + } + + const timestamp = runtime.runningTime(); + + const timeUnit = timestampFormat > 1 ? timestampFormat * 100 : timestampFormat; + const timeValue = timestamp / timeUnit; + // TODO: there's a semi complicated format conversion + // over in MicroBitLog::endRow that we might want to replicate. + // https://github.com/lancaster-university/codal-microbit-v2/blob/master/source/MicroBitLog.cpp#L405 + logData(`time (${unit})`, "" + timeValue, true /** Prepend before new headers */); + } + + currentRow.length = headers.length; + const line = currentRow.join(SEPARATOR); + if (headers.length !== committedCols) { + commitRow(headers.join(SEPARATOR), "headers") + committedCols = headers.length; + } + currentRow = undefined; + + commitRow(line, "row"); + return DAL.DEVICE_OK; + } + + export function logString(s: string) { + init() + if (!s) return + + commitRow(s, "plaintext") + } + + export function clear(fullErase: boolean) { + init() + erase(); + } + + function erase() { + headers = [] + logSize = 0; + committedCols = 0; + currentRow = undefined; + board().serialState.writeCsv("", "clear"); + } + + export function setTimeStamp(format: FlashLogTimeStampFormat) { + init() + // this option is probably not serialized, needs to move in state + timestampFormat = format + } + + export function setSerialMirroring(enabled: boolean) { + init(); + mirrorToSerial = !!enabled; + } + + export function getNumberOfRows(fromRowIndex = 0): number { + return 0 // TODO + } + + export function getRows(fromRowIndex: number, nRows: number): string { + return "" // TODO + } +} diff --git a/sim/state/microphone.ts b/sim/state/microphone.ts index d11ded6e932..b25e2fbd981 100644 --- a/sim/state/microphone.ts +++ b/sim/state/microphone.ts @@ -4,6 +4,7 @@ namespace pxsim.input { const b = microphoneState(); if (!b) return 0; b.setUsed(); + b.pingSoundLevel(); return b.getLevel(); } @@ -11,13 +12,13 @@ namespace pxsim.input { const b = microphoneState(); if (!b) return; b.setUsed(); + b.onSoundRegistered = true; pxtcore.registerWithDal(b.id, sound, body); } export function setSoundThreshold(sound: number, threshold: number){ const b = microphoneState(); if (!b) return; - b.setUsed(); if (sound === 2 /* SoundThreshold.Loud */) b.setHighThreshold(threshold); else diff --git a/sim/state/misc.ts b/sim/state/misc.ts index 91fe481ee1e..83a9a662af4 100644 --- a/sim/state/misc.ts +++ b/sim/state/misc.ts @@ -35,7 +35,7 @@ namespace pxsim.basic { namespace pxsim.control { export var inBackground = thread.runInBackground; - export function onEvent(id: number, evid: number, handler: RefAction) { + export function onEvent(id: number, evid: number, handler: RefAction, flags: number) { if (id == DAL.MICROBIT_ID_BUTTON_AB) { const b = board().buttonPairState; if (!b.usesButtonAB) { @@ -43,7 +43,7 @@ namespace pxsim.control { runtime.queueDisplayUpdate(); } } - pxtcore.registerWithDal(id, evid, handler) + pxtcore.registerWithDal(id, evid, handler, flags) } export function eventTimestamp() { diff --git a/sim/state/music.ts b/sim/state/music.ts index ce77bd1a4f4..8fcd5bc4dfc 100644 --- a/sim/state/music.ts +++ b/sim/state/music.ts @@ -11,4 +11,10 @@ namespace pxsim.music { export function setSilenceLevel(level: number) { // ignore in v1,v2 } + + export function isSoundPlaying(): boolean { + const audioActive = pxsim.AudioContextManager.isAudioElementActive(); + const soundExpressionPlaying = pxsim.codal.music.isSoundExpPlaying(); + return audioActive || soundExpressionPlaying || pxsim.record.audioIsPlaying(); + } } diff --git a/sim/state/musicalProgressions.ts b/sim/state/musicalProgressions.ts new file mode 100644 index 00000000000..c37d7c4424a --- /dev/null +++ b/sim/state/musicalProgressions.ts @@ -0,0 +1,58 @@ +namespace pxsim.music { + export interface Progression { + interval: number[]; + length: number; + } +} + +namespace pxsim.music.MusicalIntervals { + // #if CONFIG_ENABLED(JUST_SCALE) + // const float MusicalIntervals.chromaticInterval[] = [1.000000, 1.059463, 1.122462, 1.189207, 1.259921, 1.334840, 1.414214, 1.498307, 1.587401, 1.681793, 1.781797, 1.887749]; + // #else + // const float MusicalIntervals.chromaticInterval[] = [1.000000, 1.0417, 1.1250, 1.2000, 1.2500, 1.3333, 1.4063, 1.5000, 1.6000, 1.6667, 1.8000, 1.8750]; + // #endif + + export const chromaticInterval = [1.000000, 1.0417, 1.1250, 1.2000, 1.2500, 1.3333, 1.4063, 1.5000, 1.6000, 1.6667, 1.8000, 1.8750]; + + + export const majorScaleInterval = [chromaticInterval[0], chromaticInterval[2], chromaticInterval[4], chromaticInterval[5], chromaticInterval[7], chromaticInterval[9], chromaticInterval[11]]; + export const minorScaleInterval = [chromaticInterval[0], chromaticInterval[2], chromaticInterval[3], chromaticInterval[5], chromaticInterval[7], chromaticInterval[8], chromaticInterval[10]]; + export const pentatonicScaleInterval = [chromaticInterval[0], chromaticInterval[2], chromaticInterval[4], chromaticInterval[7], chromaticInterval[9]]; + export const majorTriadInterval = [chromaticInterval[0], chromaticInterval[4], chromaticInterval[7]]; + export const minorTriadInterval = [chromaticInterval[0], chromaticInterval[3], chromaticInterval[7]]; + export const diminishedInterval = [chromaticInterval[0], chromaticInterval[3], chromaticInterval[6], chromaticInterval[9]]; + export const wholeToneInterval = [chromaticInterval[0], chromaticInterval[2], chromaticInterval[4], chromaticInterval[6], chromaticInterval[8], chromaticInterval[10]]; + +} + + + +namespace pxsim.music.MusicalProgressions { + + export const chromatic: Progression = { interval: MusicalIntervals.chromaticInterval, length: 12 }; + export const majorScale: Progression = { interval: MusicalIntervals.majorScaleInterval, length: 7 }; + export const minorScale: Progression = { interval: MusicalIntervals.minorScaleInterval, length: 7 }; + export const pentatonicScale: Progression = { interval: MusicalIntervals.pentatonicScaleInterval, length: 5 }; + export const majorTriad: Progression = { interval: MusicalIntervals.majorTriadInterval, length: 3 }; + export const minorTriad: Progression = { interval: MusicalIntervals.minorTriadInterval, length: 3 }; + export const diminished: Progression = { interval: MusicalIntervals.diminishedInterval, length: 4 }; + export const wholeTone: Progression = { interval: MusicalIntervals.wholeToneInterval, length: 6 }; + + + + /** + * Determine the frequency of a given note in a given progressions + * + * @param root The root frequency of the progression + * @param progression The Progression to use + * @param offset The offset (interval) of the note to generate + * @return The frequency of the note requested in Hz. + */ + export function calculateFrequencyFromProgression(root: number, progression: Progression, offset: number) + { + let octave = Math.floor(offset / progression.length); + let index = offset % progression.length; + + return root * Math.pow(2, octave) * progression.interval[index]; + } +} \ No newline at end of file diff --git a/sim/state/record-audio.ts b/sim/state/record-audio.ts new file mode 100644 index 00000000000..41f05c6a532 --- /dev/null +++ b/sim/state/record-audio.ts @@ -0,0 +1,323 @@ +namespace pxsim { + export class RecordingState { + currentlyRecording = false; + stream: MediaStream; + recorder: MediaRecorder; + chunks: Blob[]; + + audioURL: string; + // The inputBitRate when the current audioUrl was recorded + audioURLBitRate: number; + audioClippingThreshold: number = 0.08; + + recording: HTMLAudioElement; + audioPlaying: boolean = false; + recordTimeoutID: any; + currentlyErasing: boolean; + + inputBitRate = record.defaultBitRate(); + outputBitRate = record.defaultBitRate(); + + handleAudioPlaying = () => { + this.audioPlaying = true; + }; + + handleAudioStopped = () => { + this.audioPlaying = false; + }; + + initListeners = () => { + if (this.recording) { + this.recording.addEventListener("play", this.handleAudioPlaying, false); + this.recording.addEventListener("ended", this.handleAudioStopped, false); + } + } + } +} +namespace pxsim.record { + // Arbitrarily chosen lower bound. Can't go much lower than this without bugs cropping up + const MIN_BIT_RATE = 3000; + // This is double the default in chrome (128000) + const MAX_BIT_RATE = 256000; + + const MAX_SAMPLE_RATE = 22000; + const MIN_SAMPLE_RATE = 1000; + + const MIN_RECORDING_TIME = 2000; + const MAX_RECORDING_TIME = 10000; + + let _initialized = false; + function init() { + if (!_initialized) { + registerSimStop(); + _initialized = true; + } + } + + function stopRecorder(b: DalBoard): void { + const state = b.recordingState; + state.recorder.stop(); + state.currentlyRecording = false; + runtime.queueDisplayUpdate(); + if (state.stream.active) { + for (const track of state.stream.getAudioTracks()) { + track.stop(); + track.enabled = false; + } + } + } + + async function populateRecording(b: DalBoard) { + const state = b.recordingState; + + if (state.currentlyErasing) { + await erasingAsync(b); + } + if (state.chunks[0].size > 0) { + state.audioURL = null; + const recordingType = pxsim.isSafari() ? "audio/mp4" : "audio/ogg; codecs=opus"; + const blob = new Blob(state.chunks, { type: recordingType }); + state.audioURL = window.URL.createObjectURL(blob); + } + state.currentlyRecording = false; + state.recorder = null; + state.chunks = []; + } + + export async function record(): Promise { + let b = board(); + init(); + + const state = b.recordingState; + + if (state.recorder) { + state.recorder.stop(); + clearTimeout(state.recordTimeoutID); + } + + if (navigator.mediaDevices?.getUserMedia) { + try { + state.stream = await navigator.mediaDevices.getUserMedia({ video: false, audio: true }); + state.recorder = new MediaRecorder(state.stream, { audioBitsPerSecond: state.inputBitRate }); + state.recorder.start(); + state.currentlyRecording = true; + runtime.queueDisplayUpdate(); + const recordBitRate = state.inputBitRate; + + const duration = (1 - ((recordBitRate - MIN_BIT_RATE) / (MAX_BIT_RATE - MIN_BIT_RATE))) * (MAX_RECORDING_TIME - MIN_RECORDING_TIME) + MIN_RECORDING_TIME; + + state.recordTimeoutID = setTimeout(() => { + stopRecorder(b); + }, duration) + + state.recorder.ondataavailable = (e: BlobEvent) => { + state.chunks.push(e.data); + } + + state.recorder.onstop = async () => { + await populateRecording(b); + state.audioURLBitRate = recordBitRate; + } + + } catch (error) { + console.log("An error occurred, could not get microphone access"); + if (state.recorder) { + state.recorder.stop(); + } + state.currentlyRecording = false; + } + + } else { + console.log("getUserMedia not supported on your browser!"); + state.currentlyRecording = false; + } + } + + function stopAudio() { + const b = board(); + if (!b) return; + if (b.recordingState.currentlyRecording && b.recordingState.recordTimeoutID) { + clearTimeout(b.recordingState.recordTimeoutID); + if (b.recordingState.recorder) { + stopRecorder(b); + } + } else if (b.recordingState.recording && b.recordingState.audioPlaying) { + b.recordingState.handleAudioStopped(); + stopPlayback(); + } + } + + function registerSimStop() { + pxsim.AudioContextManager.onStopAll(() => { + const b = board(); + if (b && b.recordingState && b.recordingState.recording) { + stopAudio(); + b.recordingState.recording.removeEventListener("play", b.recordingState.handleAudioPlaying); + b.recordingState.recording.removeEventListener("ended", b.recordingState.handleAudioStopped); + } + }); + } + + export function play(): void { + const b = board(); + if (!b) return; + init(); + + stopAudio(); + + const state = b.recordingState; + const volume = AudioContextManager.isMuted() ? 0 : Math.round((music.volume() / 0xff) * 100) / 100; + state.recording = AudioContextManager.createAudioSourceNode(state.audioURL, state.audioClippingThreshold, volume); + state.initListeners(); + + state.audioPlaying = true; + setTimeout(async () => { + if (!state.currentlyErasing && state.recording) { + try { + + const minPlaybackRate = 0.15 + + // 15 is the maximum playback rate that still produced sound in Chrome on Windows. + // In Firefox, it seems like 8 is the max. Higher numbers silently fail. + let maxPlaybackRate = 15; + if (isFirefox()) { + maxPlaybackRate = 8; + } + + const playbackRate = Math.max(minPlaybackRate, + Math.min( + maxPlaybackRate, + bitRateToSampleRate(state.outputBitRate) / bitRateToSampleRate(state.audioURLBitRate) + ) + ); + + state.recording.playbackRate = playbackRate; + state.recording.preservesPitch = false; + await state.recording.play(); + } + catch (e) { + if (!(e instanceof DOMException)) { + throw e; + } + } + } + else { + state.audioPlaying = false; + } + }, 10) + } + + export function stop(): void { + stopAudio(); + } + + function stopPlayback(): void { + const b = board(); + if (!b) return; + b.recordingState.recording.pause(); + b.recordingState.recording.currentTime = 0; + b.recordingState.recording.removeEventListener("play", b.recordingState.handleAudioPlaying); + b.recordingState.recording.removeEventListener("ended", b.recordingState.handleAudioStopped); + } + + function erasingAsync(b: DalBoard): Promise { + return new Promise((resolve, reject) => { + if (b.recordingState.recording && b.recordingState.audioPlaying) { + stopPlayback(); + } + if (b.recordingState.audioURL) { + window.URL.revokeObjectURL(b.recordingState.audioURL); + b.recordingState.recording = null; + } + b.recordingState.audioPlaying = false; + resolve(null); + b.recordingState.currentlyErasing = false; + }) + } + + export function erase(): void { + const b = board(); + if (!b) return; + b.recordingState.chunks = []; + b.recordingState.currentlyErasing = true; + } + + export function setMicrophoneGain(gain: number): void { + const b = board(); + if (!b) return; + const tolerance = 0.1 * Number.EPSILON; + if (gain - 0.079 < tolerance) { // low mic sensitivity + b.recordingState.audioClippingThreshold = 0.08; + } else if (gain - 0.2 < tolerance) { // mid mic sensitivity + b.recordingState.audioClippingThreshold = 0.03; + } else if (gain - 1.0 < tolerance) { // high mic sensitivity + b.recordingState.audioClippingThreshold = 0.01; + } + } + + export function audioDuration(sampleRate: number): number { + return 0; + } + + export function audioIsPlaying(): boolean { + const b = board(); + if (!b) return false; + return b.recordingState.audioPlaying; + } + + export function audioIsRecording(): boolean { + const b = board(); + if (!b) return false; + return b.recordingState.recorder ? b.recordingState.recorder.state === "recording" : false; + } + + export function audioIsStopped(): boolean { + const b = board(); + if (!b) return true; + const isNotPlaying = !audioIsPlaying(); + const isNotRecording = !audioIsRecording(); + return b.recordingState.recording ? isNotPlaying && isNotRecording : false; + } + + export function setInputSampleRate(sampleRate: number): void { + const b = board(); + if (!b) return; + + b.recordingState.inputBitRate = sampleRateToBitRate(sampleRate); + } + + export function setOutputSampleRate(sampleRate: number): void { + const b = board(); + if (!b) return; + + b.recordingState.outputBitRate = sampleRateToBitRate(sampleRate); + } + + export function setBothSamples(sampleRate: number): void { + setInputSampleRate(sampleRate); + setOutputSampleRate(sampleRate); + } + + /** + * The browser API doesn't allow us to control sample rate directly, but we + * can affect it by setting the bit rate. This maps the supported sample rates + * into a reasonable range of bit rates. + */ + function sampleRateToBitRate(sampleRate: number) { + return mapRange(sampleRate, MIN_SAMPLE_RATE, MAX_SAMPLE_RATE, MIN_BIT_RATE, MAX_BIT_RATE); + } + + function bitRateToSampleRate(bitRate: number) { + return mapRange(bitRate, MIN_BIT_RATE, MAX_BIT_RATE, MIN_SAMPLE_RATE, MAX_SAMPLE_RATE); + } + + function mapRange(value: number, inMin: number, inMax: number, outMin: number, outMax: number) { + value = Math.min(Math.max(inMin, value), inMax); + + return ((value - inMin) / (inMax - inMin)) * (outMax - outMin) + outMin; + } + + export function defaultBitRate() { + return sampleRateToBitRate(11000); + } +} \ No newline at end of file diff --git a/sim/state/serial.ts b/sim/state/serial.ts index 0a00c66048c..93f1dca384c 100644 --- a/sim/state/serial.ts +++ b/sim/state/serial.ts @@ -8,7 +8,7 @@ namespace pxsim { } private handleMessage(msg: SimulatorMessage) { - if (msg.type === "seria") { + if (msg.type === "serial") { const data = (msg).data || ""; this.receiveData(data); } @@ -36,6 +36,16 @@ namespace pxsim { this.serialOutBuffer = ''; } } + + writeCsv(s: string, type: "headers" | "row" | "clear") { + Runtime.postMessage({ + type: 'serial', + data: s, + id: runtime.id, + csvType: type, + sim: true + }) + } } } @@ -87,4 +97,8 @@ namespace pxsim.serial { export function setBaudRate(rate: number) { // TODO } + + export function writeDmesg() { + // TODO + } } \ No newline at end of file diff --git a/sim/state/soundexpression.ts b/sim/state/soundexpression.ts index 1ede75a1cf4..da895f43d23 100644 --- a/sim/state/soundexpression.ts +++ b/sim/state/soundexpression.ts @@ -1,60 +1,58 @@ namespace pxsim.music { - function loadWavAsync(path: string): Promise { - return new Promise((resolve, reject) => { - let httprequest = new XMLHttpRequest(); - httprequest.responseType = "arraybuffer"; - httprequest.onreadystatechange = function () { - if (httprequest.readyState == XMLHttpRequest.DONE) { - if (httprequest.status == 200) { - const r = httprequest.response; - resolve(new Uint8Array(httprequest.response)); - } - else { - reject(httprequest.status); - } - } - }; - httprequest.open("GET", path, true); - httprequest.send(); - }) - } - const wavPromises: Map> = {} //% export function __playSoundExpression(notes: string, waitTillDone: boolean): void { - const cb = getResume(); - const b = board(); - // v2 only... - b.ensureHardwareVersion(2); - - // load wav file - let p: Promise; - // defined in sim.html - const path = (pxsim).soundExpressionFiles[notes]; - if (path) { - p = wavPromises[notes] || (wavPromises[notes] = loadWavAsync(path)); - } else - p = Promise.resolve(undefined); - - p.then(data => { - // failed to load data - if (data) { - // finally play - const buf = new RefBuffer(data); - const pp = AudioContextManager.playBufferAsync(buf) - if (waitTillDone) - // wait until sound is done - return pp; - } - // don't wait - return Promise.resolve(); - }).catch((e) => { - console.log(e) - }).finally(() => { - cb(); - }) + notes = lookupBuiltIn(notes); + // Volume is multiplied by 0.03 so that it matches the output of other audio blocks. + const volume = (pxsim.music.volume() / 0xff) * 0.03; + pxsim.codal.music.__playSoundExpression(notes, waitTillDone, volume); } export function __stopSoundExpressions() { - AudioContextManager.stopAll(); + pxsim.codal.music.__stopSoundExpressions(); + } + + const giggle = "giggle"; + const giggleData = "010230988019008440044008881023001601003300240000000000000000000000000000,110232570087411440044008880352005901003300010000000000000000010000000000,310232729021105440288908880091006300000000240700020000000000003000000000,310232729010205440288908880091006300000000240700020000000000003000000000,310232729011405440288908880091006300000000240700020000000000003000000000"; + const happy = "happy"; + const happyData = "010231992066911440044008880262002800001800020500000000000000010000000000,002322129029508440240408880000000400022400110000000000000000007500000000,000002129029509440240408880145000400022400110000000000000000007500000000"; + const hello = "hello"; + const helloData = "310230673019702440118708881023012800000000240000000000000000000000000000,300001064001602440098108880000012800000100040000000000000000000000000000,310231064029302440098108881023012800000100040000000000000000000000000000"; + const mysterious = "mysterious"; + const mysteriousData = "400002390033100440240408880477000400022400110400000000000000008000000000,405512845385000440044008880000012803010500160000000000000000085000500015"; + const sad = "sad"; + const sadData = "310232226070801440162408881023012800000100240000000000000000000000000000,310231623093602440093908880000012800000100240000000000000000000000000000"; + const slide = "slide"; + const slideData = "105202325022302440240408881023012801020000110400000000000000010000000000,010232520091002440044008881023012801022400110400000000000000010000000000"; + const soaring = "soaring"; + const soaringData = "210234009530905440599908881023002202000400020250000000000000020000000000,402233727273014440044008880000003101024400030000000000000000000000000000"; + const spring = "spring"; + const springData = "306590037116312440058708880807003400000000240000000000000000050000000000,010230037116313440058708881023003100000000240000000000000000050000000000"; + const twinkle = "twinkle"; + const twinkleData = "010180007672209440075608880855012800000000240000000000000000000000000000"; + const yawn = "yawn"; + const yawnData = "200002281133202440150008881023012801024100240400030000000000010000000000,005312520091002440044008880636012801022400110300000000000000010000000000,008220784019008440044008880681001600005500240000000000000000005000000000,004790784019008440044008880298001600000000240000000000000000005000000000,003210784019008440044008880108001600003300080000000000000000005000000000"; + + function lookupBuiltIn(sound: string) { + if (sound == giggle) + return giggleData; + if (sound == happy) + return happyData; + if (sound == hello) + return helloData; + if (sound == mysterious) + return mysteriousData; + if (sound == sad) + return sadData; + if (sound == slide) + return slideData; + if (sound == soaring) + return soaringData; + if (sound == spring) + return springData; + if (sound == twinkle) + return twinkleData; + if (sound == yawn) + return yawnData; + return sound; } } \ No newline at end of file diff --git a/sim/tsconfig.json b/sim/tsconfig.json index 1267fda9edc..dd740064b29 100644 --- a/sim/tsconfig.json +++ b/sim/tsconfig.json @@ -1,16 +1,21 @@ { "compilerOptions": { - "target": "es5", + "target": "es2017", "noImplicitAny": true, "noImplicitReturns": true, "declaration": true, - "out": "../built/sim.js", - "rootDir": ".", + "outFile": "../built/sim.js", + "rootDir": "..", "newLine": "LF", + "moduleResolution": "node", "sourceMap": false, - "lib": ["dom", "dom.iterable", "scripthost", "es6"], - "types": ["bluebird"], - "typeRoots": ["../node_modules/@types"] + "lib": [ + "dom", + "dom.iterable", + "scripthost", + "es2017" + ], + "types": [] }, "include": [ "*.ts", diff --git a/sim/visuals/microbit.ts b/sim/visuals/microbit.ts index fdef7e9eb4b..939b41943f7 100644 --- a/sim/visuals/microbit.ts +++ b/sim/visuals/microbit.ts @@ -17,6 +17,9 @@ namespace pxsim.visuals { .sim-button { pointer-events: none; } + .sim-head .sim-button { + pointer-events: unset; + } .sim-board, .sim-display, sim-button { fill: #111; } @@ -31,13 +34,14 @@ namespace pxsim.visuals { .sim-button-nut:hover { stroke:1px solid #704A4A; } - .sim-pin:hover { + .sim-pin[focusable=true]:hover { stroke:#D4AF37; stroke-width:2px; } - .sim-pin-touch.touched:hover { - stroke:darkorange; + .sim-pin-touch[focusable=true].touched { + stroke:darkorange !important; + stroke-width:5px; } .sim-led-back:hover { @@ -132,6 +136,7 @@ namespace pxsim.visuals { } .sim-label, .sim-button-label { fill: #000; + pointer-events: none; } .sim-wireframe .sim-board { stroke-width: 2px; @@ -139,12 +144,26 @@ namespace pxsim.visuals { *:focus { outline: none; } - *:focus .sim-button-outer, - .sim-pin:focus, - .sim-thermometer:focus, - .sim-shake:focus, - .sim-light-level-button:focus { - stroke: #4D90FE; + *:focus-visible .sim-button-outer, + .sim-shake:focus-visible, + .sim-thermometer:focus-visible { + outline: 5px solid white; + stroke: black; + stroke-width: 10px; + paint-order: stroke; + } + .sim-button-outer.sim-button-group:focus-visible > .sim-button { + outline: 5px solid white; + stroke: black; + stroke-width: 5px; + paint-order: stroke; + } + .sim-light-level-button:focus-visible, + .sim-antenna-outer:focus-visible > .sim-antenna { + outline: 5px solid white; + } + .sim-pin:focus-visible { + stroke: white; stroke-width: 5px !important; } .no-drag, .sim-text, .sim-text-small, @@ -156,6 +175,9 @@ namespace pxsim.visuals { -webkit-user-select: none; -ms-user-select: none; } + [focusable=true] { + cursor: pointer; + } `; const MB_HIGHCONTRAST = ` path.sim-board { @@ -165,11 +187,14 @@ path.sim-board { .sim-led { stroke: red; } -*:focus .sim-button-outer, -.sim-pin:focus, -.sim-thermometer:focus, -.sim-shake:focus, -.sim-light-level-button:focus { +.sim-led-back { + stroke: white; +} +*:focus-visible .sim-button-outer, +.sim-pin:focus-visible, +.sim-thermometer:focus-visible, +.sim-shake:focus-visible, +.sim-light-level-button:focus-visible { stroke: #10C8CD !important; } ` @@ -189,32 +214,51 @@ path.sim-board { "P0", "P1", "P2", "P3", "P4", "P5", "P6", "P7", "P8", "P9", "P10", "P11", "P12", "P13", "P14", "P15", "P16", "P17", "P18", "P19", "P20", "GND0", "GND", "+3v3", "GND1"]; - const pinTitles = [ - "P0, ANALOG IN", - "P1, ANALOG IN", - "P2, ANALOG IN", - "P3, ANALOG IN, LED Col 1", - "P4, ANALOG IN, LED Col 2", - "P5, BUTTON A", - "P6, LED Col 9", - "P7, LED Col 8", - "P8", - "P9, LED Col 7", - "P10, ANALOG IN, LED Col 3", - "P11, BUTTON B", - "P12, RESERVED ACCESSIBILITY", - "P13, SPI - SCK", - "P14, SPI - MISO", - "P15, SPI - MOSI", - "P16, SPI - Chip Select", - "P17, +3v3", - "P18, +3v3", - "P19, I2C - SCL", - "P20, I2C - SDA", - "GND", "GND", "+3v3", "GND" + const pinDrawOrder = [ + "P3", "P0", "P4", "P5", "P6", "P7", "P1", "P8", "P9", "P10", "P11", + "P12", "P2", "P13", "P14", "P15", "P16", "P17", "P18", "P19", "P20", + "GND0", "GND", "+3v3", "GND1" + ]; + interface PinTitle { + title: string, + ariaLabel: string + } + // title is currently unused. + const pinTitles: PinTitle[] = [ + { title: "P0, ANALOG IN", ariaLabel: pxsim.localization.lf("Pin 0") }, + { title: "P1, ANALOG IN", ariaLabel: pxsim.localization.lf("Pin 1") }, + { title: "P2, ANALOG IN", ariaLabel: pxsim.localization.lf("Pin 2") }, + { title: "P3, ANALOG IN, LED Col 1", ariaLabel: pxsim.localization.lf("Pin 3") }, + { title: "P4, ANALOG IN, LED Col 2", ariaLabel: pxsim.localization.lf("Pin 4") }, + { title: "P5, BUTTON A", ariaLabel: pxsim.localization.lf("Pin 5") }, + { title: "P6, LED Col 9", ariaLabel: pxsim.localization.lf("Pin 6") }, + { title: "P7, LED Col 8", ariaLabel: pxsim.localization.lf("Pin 7") }, + { title: "P8", ariaLabel: pxsim.localization.lf("Pin 8") }, + { title: "P9, LED Col 7", ariaLabel: pxsim.localization.lf("Pin 9") }, + { title: "P10, ANALOG IN, LED Col 3", ariaLabel: pxsim.localization.lf("Pin 10") }, + { title: "P11, BUTTON B", ariaLabel: pxsim.localization.lf("Pin 11") }, + { title: "P12, RESERVED ACCESSIBILITY", ariaLabel: pxsim.localization.lf("Pin 12") }, + { title: "P13, SPI - SCK", ariaLabel: pxsim.localization.lf("Pin 13") }, + { title: "P14, SPI - MISO", ariaLabel: pxsim.localization.lf("Pin 14") }, + { title: "P15, SPI - MOSI", ariaLabel: pxsim.localization.lf("Pin 15") }, + { title: "P16, SPI - Chip Select", ariaLabel: pxsim.localization.lf("Pin 16") }, + { title: "P17, +3v3", ariaLabel: pxsim.localization.lf("Pin 3V") }, + { title: "P18, +3v3", ariaLabel: pxsim.localization.lf("Pin 3V") }, + { title: "P19, I2C - SCL", ariaLabel: pxsim.localization.lf("Pin 19") }, + { title: "P20, I2C - SDA", ariaLabel: pxsim.localization.lf("Pin 20") }, + { title: "GND", ariaLabel: pxsim.localization.lf("Pin GND") }, + { title: "GND", ariaLabel: pxsim.localization.lf("Pin GND") }, + { title: "+3v3", ariaLabel: pxsim.localization.lf("Pin 3V") }, + { title: "GND", ariaLabel: pxsim.localization.lf("Pin GND") }, ]; const MB_WIDTH = 500; const MB_HEIGHT = 408; + + const LIGHT_LEVEL_BUTTON_POSITION_Y = 50; + const LIGHT_LEVEL_BUTTON_RADIUS = 35; + const ANTENNA_X = 380; + const ANTENNA_WAVE_PERIOD_X = 18; + const ANTENNA_WAVE_COUNT = 5; export interface IBoardTheme { highContrast?: boolean; accent?: string; @@ -258,9 +302,8 @@ path.sim-board { if (highContrast) { theme = JSON.parse(JSON.stringify(theme)) as IBoardTheme; theme.highContrast = true; - theme.ledOff = "#888"; - theme.ledOn = "#0000bb"; - theme.display = "#ffffff"; + theme.ledOff = "#000000"; + theme.ledOn = "#FF0000"; theme.pin = "#D4AF37"; theme.accent = "#FFD43A"; } @@ -273,8 +316,15 @@ path.sim-board { wireframe?: boolean; } + interface EventBinding { + event: string; + handler: (ev: Event) => void; + element?: Element | Document; + } + export class MicrobitBoardSvg implements BoardView { public element: SVGSVGElement; + private liveRegionInitialized = false; private style: SVGStyleElement; private defs: SVGDefsElement; private g: SVGGElement; @@ -296,18 +346,23 @@ path.sim-board { private leds: SVGElement[]; private microphoneLed: SVGElement; private systemLed: SVGCircleElement; - private antenna: SVGPolylineElement; + private antenna: SVGElement; + private antennaInitialized = false; private rssi: SVGTextElement; private lightLevelButton: SVGCircleElement; private lightLevelGradient: SVGLinearGradientElement; + private lightLevelInitialized = false; private lightLevelText: SVGTextElement; private thermometerGradient: SVGLinearGradientElement; private thermometer: SVGRectElement; + private thermometerInitialized = false; private thermometerText: SVGTextElement; private soundLevelGradient: SVGLinearGradientElement; private soundLevel: SVGRectElement; + private soundLevelInitialized = false; private soundLevelText: SVGTextElement; private shakeButton: SVGCircleElement; + private shakeInitialized = false; private shakeText: SVGTextElement; private accTextX: SVGTextElement; private accTextY: SVGTextElement; @@ -317,6 +372,23 @@ path.sim-board { public board: pxsim.DalBoard; private pinNmToCoord: Map = {}; private domHardwareVersion = 1; + private bindings: EventBinding[] = []; + private moveHeadingOnClick = (ev: MouseEvent) => { + let pt = this.element.createSVGPoint(); + let cur = svg.cursorPoint(pt, this.element, ev); + const logoBounds = this.head.getBBox(); + const logoCenterX = logoBounds.x + (logoBounds.width / 2); + const logoCenterY = logoBounds.y + (logoBounds.height / 2); + const distance = Math.sqrt((((cur.y - logoCenterY) ** 2) + ((cur.x - logoCenterX) ** 2))); + + // 30 and 90 are not precise, just numbers that fit nicely with usage + if (distance > 30 && distance < 90) { + const state = this.board; + state.compassState.heading = Math.floor(Math.atan2(cur.y - logoCenterY, cur.x - logoCenterX) * 180 / Math.PI) + 90; + if (state.compassState.heading < 0) state.compassState.heading += 360; + this.updateHeading(); + } + } constructor(public props: IBoardProps) { this.recordPinCoords(); @@ -330,7 +402,7 @@ path.sim-board { if (props && props.runtime) { this.board = this.props.runtime.board as pxsim.DalBoard; this.board.updateSubscribers.push(() => this.updateState()); - this.updateState(); + this.updateState(true); this.attachEvents(); } } @@ -365,6 +437,15 @@ path.sim-board { }); } + public removeEventListeners() { + for (const binding of this.bindings) { + const el = binding.element || document; + el.removeEventListener(binding.event, binding.handler); + } + + document.body.removeEventListener(pointerEvents.down[0], this.moveHeadingOnClick); + } + private updateTheme() { let theme = this.props.theme; @@ -380,11 +461,13 @@ path.sim-board { svg.fill(this.buttonsOuter[2], theme.virtualButtonOuter); svg.fill(this.buttons[2], theme.virtualButtonUp); svg.fills(this.logos, theme.accent); - if (this.domHardwareVersion > 1) + if (this.domHardwareVersion > 1) { svg.fills(this.heads.slice(1), "gold"); - else + } else { svg.fills(this.heads.slice(1), theme.accent); - if (this.shakeButton) svg.fill(this.shakeButton, theme.virtualButtonUp); + } + + svg.fill(this.shakeButton, theme.virtualButtonUp); this.pinGradients.forEach(lg => svg.setGradientColors(lg, theme.pin, theme.pinActive)); svg.setGradientColors(this.lightLevelGradient, theme.lightLevelOn, theme.lightLevelOff); @@ -394,12 +477,13 @@ path.sim-board { this.positionV2Elements(); } - public updateState() { + public updateState(initialCall: boolean = false) { const state = this.board; if (!state) return; this.updateHardwareVersion(); this.updateMicrophone(); + this.updateRecordingActive(); this.updateButtonPairs(); this.updateLEDMatrix(); this.updatePins(); @@ -415,16 +499,23 @@ path.sim-board { U.addClass(this.element, "grayscale"); else U.removeClass(this.element, "grayscale"); + + if (!initialCall && !this.liveRegionInitialized) { + // The iframe document's innerHTML is cleared after mkBoardView is called. + // Ensure that the live region is created after this. + accessibility.setLiveContent(""); + this.liveRegionInitialized = true; + } } private updateButtonPairs() { const state = this.board; - const theme = this.props.theme; - const bpState = state.buttonPairState; - const buttons = [bpState.aBtn, bpState.bBtn, bpState.abBtn]; - buttons.forEach((btn, index) => { - svg.fill(this.buttons[index], btn.pressed ? theme.buttonDown : theme.buttonUp); - }); + const { buttonDown, buttonUp, virtualButtonUp } = this.props.theme; + const { aBtn, bBtn, abBtn } = state.buttonPairState; + svg.fill(this.buttons[0], aBtn.pressed ? buttonDown : buttonUp); + svg.fill(this.buttons[1], bBtn.pressed ? buttonDown : buttonUp); + svg.fill(this.buttons[2], abBtn.pressed ? buttonDown : virtualButtonUp); + svg.fill(this.headParts, state.logoTouch.pressed ? buttonDown : buttonUp); } private updateLEDMatrix() { @@ -456,8 +547,9 @@ path.sim-board { private updateGestures() { let state = this.board; - if (state.accelerometerState.useShake && !this.shakeButton) { - this.shakeButton = svg.child(this.g, "circle", { cx: 404, cy: 115, r: 12, class: "sim-shake" }) as SVGCircleElement; + if (state.accelerometerState.useShake && !this.shakeInitialized) { + this.shakeInitialized = true; + this.shakeButton.style.visibility = "visible"; accessibility.makeFocusable(this.shakeButton); svg.fill(this.shakeButton, this.props.theme.virtualButtonUp) pointerEvents.down.forEach(evid => this.shakeButton.addEventListener(evid, ev => { @@ -473,11 +565,17 @@ path.sim-board { svg.fill(this.shakeButton, this.props.theme.virtualButtonUp); this.board.accelerometerState.shake(); }) - accessibility.enableKeyboardInteraction(this.shakeButton, undefined, () => { - this.board.accelerometerState.shake(); - }); - accessibility.setAria(this.shakeButton, "button", "Shake the board"); - this.shakeText = svg.child(this.g, "text", { x: 420, y: 122, class: "sim-text-small" }) as SVGTextElement; + accessibility.enableKeyboardInteraction(this.shakeButton, + () => { // keydown + svg.fill(this.shakeButton, this.props.theme.buttonDown); + }, + () => { // keyup + svg.fill(this.shakeButton, this.props.theme.virtualButtonUp); + this.board.accelerometerState.shake(); + } + ); + accessibility.setAria(this.shakeButton, "button", pxsim.localization.lf("Shake")); + this.shakeText = svg.child(this.g, "text", { x: 420, y: 122, class: "sim-text-small", "aria-hidden": true }) as SVGTextElement; this.shakeText.textContent = "SHAKE"; } } @@ -491,6 +589,23 @@ path.sim-board { this.updateSoundLevel(); } + private updateRecordingActive() { + const b = board(); + if (!b) + return; + + let theme = this.props.theme; + if (this.microphoneLed) { + if (b.recordingState.currentlyRecording || b.microphoneState.soundLevelRequested) { + svg.fills([this.microphoneLed], theme.ledOn); + svg.filter(this.microphoneLed, `url(#ledglow)`); + } else if (!(b.microphoneState.onSoundRegistered || b.microphoneState.soundLevelRequested)) { + svg.fills([this.microphoneLed], theme.ledOff); + svg.filter(this.microphoneLed, `url(#none)`); + } + } + } + private updateButtonAB() { let state = this.board; if (state.buttonPairState.usesButtonAB && !this.buttonABText) { @@ -507,13 +622,16 @@ path.sim-board { if (!pin) return; let text = this.pinTexts[index]; let v = ""; + let ariaValueNow: number; if (pin.mode & PinFlags.Analog) { v = Math.floor(100 - (pin.value || 0) / 1023 * 100) + "%"; if (text) text.textContent = (pin.period ? "~" : "") + (pin.value || 0) + ""; + ariaValueNow = pin.value ?? 0; } else if (pin.mode & PinFlags.Digital) { v = pin.value > 0 ? "0%" : "100%"; if (text) text.textContent = pin.value > 0 ? "1" : "0"; + ariaValueNow = pin.value > 0 ? 1 : 0; } else if (pin.mode & PinFlags.Touch) { v = pin.touched ? "0%" : "100%"; @@ -526,12 +644,34 @@ path.sim-board { if (pin.mode !== PinFlags.Unused) { accessibility.makeFocusable(this.pins[index]); - accessibility.setAria(this.pins[index], "slider", this.pins[index].firstChild.textContent); - this.pins[index].setAttribute("aria-valuemin", "0"); - this.pins[index].setAttribute("aria-valuemax", pin.mode & PinFlags.Analog ? "1023" : "100"); - this.pins[index].setAttribute("aria-orientation", "vertical"); - this.pins[index].setAttribute("aria-valuenow", text ? text.textContent : v); - accessibility.setLiveContent(text ? text.textContent : v); + if (pin.mode & PinFlags.Touch) { + this.pins[index].setAttribute("role", "button"); + this.pins[index].ariaLabel = this.pins[index].firstChild.textContent + this.pins[index].removeAttribute("aria-valuemin"); + this.pins[index].removeAttribute("aria-valuemax"); + this.pins[index].removeAttribute("aria-orientation"); + this.pins[index].removeAttribute("aria-valuenow"); + this.pins[index].removeAttribute("aria-valuetext"); + this.pins[index].removeAttribute("aria-readonly"); + } else { + this.pins[index].setAttribute("role", "slider"); + this.pins[index].ariaLabel = this.pins[index].firstChild.textContent; + this.pins[index].setAttribute("aria-valuemin", "0"); + this.pins[index].setAttribute("aria-valuemax", pin.mode & PinFlags.Analog ? "1023" : "1"); + this.pins[index].setAttribute("aria-orientation", "vertical"); + this.pins[index].setAttribute("aria-valuenow", ariaValueNow.toString() ?? ""); + // Check that the text content isn't just a plain int and only set aria-valuetext if required. + if (text?.textContent && text?.textContent !== parseInt(text?.textContent).toString()) { + this.pins[index].setAttribute("aria-valuetext", text.textContent); + } else { + this.pins[index].removeAttribute("aria-valuetext"); + } + if (pin.mode & PinFlags.Input) { + this.pins[index].removeAttribute("aria-readonly"); + } else { + this.pins[index].setAttribute("aria-readonly", "true"); + } + } } } @@ -541,19 +681,10 @@ path.sim-board { let tmin = -5; let tmax = 50; - if (!this.thermometer) { - let gid = "gradient-thermometer"; - this.thermometerGradient = svg.linearGradient(this.defs, gid); - this.thermometer = svg.child(this.g, "rect", { - class: "sim-thermometer no-drag", - x: 120, - y: 110, - width: 20, - height: 160, - rx: 5, ry: 5, - fill: `url(#${gid})` - }); - this.thermometerText = svg.child(this.g, "text", { class: 'sim-text', x: 58, y: 130 }) as SVGTextElement; + if (!this.thermometerInitialized) { + this.thermometerInitialized = true; + this.thermometer.style.visibility = "visible"; + this.thermometerText = svg.child(this.g, "text", { class: 'sim-text', x: 58, y: 130, "aria-hidden": true }) as SVGTextElement; if (this.props.runtime) this.props.runtime.environmentGlobals[pxsim.localization.lf("temperature")] = state.thermometerState.temperature; this.updateTheme(); @@ -573,24 +704,15 @@ path.sim-board { ev => { }, // keydown (ev) => { - let charCode = (typeof ev.which == "number") ? ev.which : ev.keyCode - if (charCode === 40 || charCode === 37) { // Down/Left arrow - state.thermometerState.temperature--; - if (state.thermometerState.temperature < -5) { - state.thermometerState.temperature = 50; - } - this.updateTemperature(); - } else if (charCode === 38 || charCode === 39) { // Up/Right arrow - state.thermometerState.temperature++; - if (state.thermometerState.temperature > 50) { - state.thermometerState.temperature = -5; - } + const value = commonKeyHandler(ev, state.thermometerState.temperature, tmin, tmax); + if (value !== undefined) { + state.thermometerState.temperature = value; this.updateTemperature(); } }) accessibility.makeFocusable(this.thermometer); - accessibility.setAria(this.thermometer, "slider", pxsim.localization.lf("Thermometer")); + accessibility.setAria(this.thermometer, "slider", pxsim.localization.lf("Temperature")); this.thermometer.setAttribute("aria-valuemin", "-5"); this.thermometer.setAttribute("aria-valuemax", "50"); this.thermometer.setAttribute("aria-orientation", "vertical"); @@ -604,7 +726,6 @@ path.sim-board { this.thermometerText.textContent = t + "°C"; this.thermometer.setAttribute("aria-valuenow", t.toString()); this.thermometer.setAttribute("aria-valuetext", t + "°C"); - accessibility.setLiveContent(t + "°C"); } private updateSoundLevel() { @@ -613,20 +734,11 @@ path.sim-board { const tmin = 0 // state.microphoneState.min; const tmax = 255 //state.microphoneState.max; - if (!this.soundLevel) { + if (!this.soundLevelInitialized) { + this.soundLevelInitialized = true; + this.soundLevel.style.visibility = "visible"; const level = state.microphoneState.getLevel(); - let gid = "gradient-soundlevel"; - this.soundLevelGradient = svg.linearGradient(this.defs, gid); - this.soundLevel = svg.child(this.g, "rect", { - class: "sim-thermometer no-drag", - x: 360, - y: 110, - width: 20, - height: 160, - rx: 5, ry: 5, - fill: `url(#${gid})` - }); - this.soundLevelText = svg.child(this.g, "text", { class: 'sim-text', x: 370, y: 90 }) as SVGTextElement; + this.soundLevelText = svg.child(this.g, "text", { class: 'sim-text', x: 370, y: 90, "aria-hidden": true }) as SVGTextElement; if (this.props.runtime) this.props.runtime.environmentGlobals[pxsim.localization.lf("sound level")] = state.microphoneState.getLevel(); this.updateTheme(); @@ -646,12 +758,9 @@ path.sim-board { ev => { }, // keydown (ev) => { - let charCode = (typeof ev.which == "number") ? ev.which : ev.keyCode - if (charCode === 40 || charCode === 37) { // Down/Left arrow - state.microphoneState.setLevel(state.microphoneState.getLevel() - 1); - this.updateMicrophone(); - } else if (charCode === 38 || charCode === 39) { // Up/Right arrow - state.microphoneState.setLevel(state.microphoneState.getLevel() + 1) + const value = commonKeyHandler(ev, state.microphoneState.getLevel(), tmin, tmax); + if (value !== undefined) { + state.microphoneState.setLevel(value); this.updateMicrophone(); } }) @@ -671,7 +780,6 @@ path.sim-board { this.soundLevelText.textContent = t + ""; this.soundLevel.setAttribute("aria-valuenow", t.toString()); this.soundLevel.setAttribute("aria-valuetext", t + ""); - accessibility.setLiveContent(t + ""); } private updateHeading() { @@ -686,22 +794,40 @@ path.sim-board { let pt = this.element.createSVGPoint(); svg.buttonEvents( this.head, + // move (ev: MouseEvent) => { let cur = svg.cursorPoint(pt, this.element, ev); - state.compassState.heading = Math.floor(Math.atan2(cur.y - yc, cur.x - xc) * 180 / Math.PI + 90); + state.compassState.heading = Math.floor(Math.atan2(cur.y - yc, cur.x - xc) * 180 / Math.PI) + 90; if (state.compassState.heading < 0) state.compassState.heading += 360; this.updateHeading(); - }); + }, + // start + ev => { }, + // stop + ev => { }, + // keydown + (ev) => { + const value = commonKeyHandler(ev, state.compassState.heading, 0, 359); + if (value !== undefined) { + state.compassState.heading = value; + this.updateHeading(); + } + } + ); this.headInitialized = true; } let txt = state.compassState.heading.toString() + "°"; if (txt != this.headText.textContent) { - svg.rotateElement(this.head, xc, yc, state.compassState.heading + 180); + svg.rotateElement(this.head, xc, yc, state.compassState.heading - 180); this.headText.textContent = txt; if (this.props.runtime) this.props.runtime.environmentGlobals[pxsim.localization.lf("heading")] = state.compassState.heading; } + + // make sim head focusable when there is a compass + this.headParts.setAttribute("class", "sim-button-outer sim-button-group") + accessibility.makeFocusable(this.headParts); } private lastFlashTime: number = 0; @@ -717,39 +843,46 @@ path.sim-board { private lastAntennaFlash: number = 0; public flashAntenna() { - if (!this.antenna) { - let ax = 380; - let dax = 18; - let ayt = 10; - let ayb = 40; - const wh = dax * 5; - const antenaBackground = svg.child(this.g, "rect", { x: ax, y: ayt, width: wh, height: ayb - ayt, fill: "transparent" }); - this.antenna = svg.child(this.g, "polyline", { class: "sim-antenna", points: `${ax},${ayb} ${ax},${ayt} ${ax += dax},${ayt} ${ax},${ayb} ${ax += dax},${ayb} ${ax},${ayt} ${ax += dax},${ayt} ${ax},${ayb} ${ax += dax},${ayb} ${ax},${ayt} ${ax += dax},${ayt}` }) - + if (!this.antennaInitialized) { + this.antenna.style.visibility = "visible"; + this.antennaInitialized = true; + const antennaWidth = ANTENNA_WAVE_PERIOD_X * ANTENNA_WAVE_COUNT; + const valueMin = -128; + const valueMax = -42; + const setValue = (val: number) => { + const rs = Math.max(valueMin, Math.min(valueMax, val)); + this.board.radioState.datagram.rssi = rs; + this.updateRSSI(); + } const pt = this.element.createSVGPoint(); - const evh = (ev: MouseEvent) => { + const mouseEventHandler = (ev: MouseEvent) => { const state = this.board; if (!state) return; const pos = svg.cursorPoint(pt, this.element, ev); - const rs = Math.max(-128, Math.min(-42, (-138 + (pos.x - ax + wh) / wh * 100) | 0)); - this.board.radioState.datagram.rssi = rs; - this.updateRSSI(); + setValue((-138 + (pos.x - ANTENNA_X) / antennaWidth * 100) | 0); + }; + const keyboardEventHandler = (ev: KeyboardEvent) => { + const value = commonKeyHandler(ev, this.board.radioState.datagram.rssi ?? -75, valueMin, valueMax); + if (value !== undefined) { + setValue(value); + } }; - svg.buttonEvents(antenaBackground, evh, evh, evh, (ev) => { }) - svg.buttonEvents(this.antenna, evh, evh, evh, (ev) => { }) + + svg.buttonEvents(this.antenna.children[0], mouseEventHandler, mouseEventHandler, mouseEventHandler, () => { }); + svg.buttonEvents(this.antenna.children[1], mouseEventHandler, mouseEventHandler, mouseEventHandler, () => { }); + this.antenna.addEventListener('keydown', keyboardEventHandler); accessibility.makeFocusable(this.antenna); - accessibility.setAria(this.antenna, "slider", "RSSI"); - this.antenna.setAttribute("aria-valuemin", "-128"); - this.antenna.setAttribute("aria-valuemax", "-42"); + accessibility.setAria(this.antenna, "slider", pxsim.localization.lf("Received Signal Strength Indicator"));; + this.antenna.setAttribute("aria-valuemin", `${valueMin}`); + this.antenna.setAttribute("aria-valuemax", `${valueMax}`); this.antenna.setAttribute("aria-orientation", "horizontal"); - this.antenna.setAttribute("aria-valuenow", ""); - accessibility.setLiveContent(""); + this.antenna.setAttribute("aria-valuenow", (this.board.radioState.datagram.rssi ?? -75).toString()); } let now = Date.now(); if (now - this.lastAntennaFlash > 200) { this.lastAntennaFlash = now; - svg.animate(this.antenna, 'sim-flash-stroke') + svg.animate(this.antenna.children[1] as SVGElement, 'sim-flash-stroke') } this.updateRSSI(); } @@ -761,14 +894,11 @@ path.sim-board { if (v === undefined) return; if (!this.rssi) { - let ax = 380; - let dax = 18; let ayt = 10; let ayb = 40; - const wh = dax * 5; for (let i = 0; i < 4; ++i) - svg.child(this.g, "rect", { x: ax - 90 + i * 6, y: ayt + 28 - i * 4, width: 4, height: 2 + i * 4, fill: "#fff" }) - this.rssi = svg.child(this.g, "text", { x: ax - 64, y: ayb, class: "sim-text" }) as SVGTextElement; + svg.child(this.g, "rect", { x: ANTENNA_X - 90 + i * 6, y: ayt + 28 - i * 4, width: 4, height: 2 + i * 4, fill: "#fff" }) + this.rssi = svg.child(this.g, "text", { x: ANTENNA_X - 64, y: ayb, class: "sim-text", "aria-hidden": true }) as SVGTextElement; this.rssi.textContent = ""; } @@ -776,7 +906,6 @@ path.sim-board { if (vt !== this.rssi.textContent) { this.rssi.textContent = v.toString(); this.antenna.setAttribute("aria-valuenow", this.rssi.textContent); - accessibility.setLiveContent(this.rssi.textContent); } } @@ -791,23 +920,16 @@ path.sim-board { let state = this.board; if (!state || !state.lightSensorState.usesLightLevel) return; - if (!this.lightLevelButton) { - let gid = "gradient-light-level"; - this.lightLevelGradient = svg.linearGradient(this.defs, gid) - let cy = 50; - let r = 35; - this.lightLevelButton = svg.child(this.g, "circle", { - cx: `50px`, cy: `${cy}px`, r: `${r}px`, - class: 'sim-light-level-button no-drag', - fill: `url(#${gid})` - }) as SVGCircleElement; + if (!this.lightLevelInitialized) { + this.lightLevelInitialized = true; + this.lightLevelButton.style.visibility = "visible"; let pt = this.element.createSVGPoint(); svg.buttonEvents(this.lightLevelButton, // move (ev) => { let pos = svg.cursorPoint(pt, this.element, ev); - let rs = r / 2; - let level = Math.max(0, Math.min(255, Math.floor((pos.y - (cy - rs)) / (2 * rs) * 255))); + let rs = LIGHT_LEVEL_BUTTON_RADIUS / 2; + let level = Math.max(0, Math.min(255, Math.floor((pos.y - (LIGHT_LEVEL_BUTTON_POSITION_Y - rs)) / (2 * rs) * 255))); if (level != this.board.lightSensorState.lightLevel) { this.board.lightSensorState.lightLevel = level; this.applyLightLevel(); @@ -819,28 +941,19 @@ path.sim-board { ev => { }, // keydown (ev) => { - let charCode = (typeof ev.which == "number") ? ev.which : ev.keyCode - if (charCode === 40 || charCode === 37) { // Down/Left arrow - this.board.lightSensorState.lightLevel--; - if (this.board.lightSensorState.lightLevel < 0) { - this.board.lightSensorState.lightLevel = 255; - } - this.applyLightLevel(); - } else if (charCode === 38 || charCode === 39) { // Up/Right arrow - this.board.lightSensorState.lightLevel++; - if (this.board.lightSensorState.lightLevel > 255) { - this.board.lightSensorState.lightLevel = 0; - } - this.applyLightLevel(); + const value = commonKeyHandler(ev, state.lightSensorState.lightLevel, 0, 255); + if (value !== undefined) { + state.lightSensorState.lightLevel = value; + this.updateLightLevel(); } }); - this.lightLevelText = svg.child(this.g, "text", { x: 85, y: cy + r - 5, text: '', class: 'sim-text' }) as SVGTextElement; + this.lightLevelText = svg.child(this.g, "text", { x: 85, y: LIGHT_LEVEL_BUTTON_POSITION_Y + LIGHT_LEVEL_BUTTON_RADIUS - 5, text: '', class: 'sim-text', 'aria-hidden': true }) as SVGTextElement; if (this.props.runtime) this.props.runtime.environmentGlobals[pxsim.localization.lf("lightLevel")] = state.lightSensorState.lightLevel; this.updateTheme(); accessibility.makeFocusable(this.lightLevelButton); - accessibility.setAria(this.lightLevelButton, "slider", "Light level"); + accessibility.setAria(this.lightLevelButton, "slider", pxsim.localization.lf("Light level")); this.lightLevelButton.setAttribute("aria-valuemin", "0"); this.lightLevelButton.setAttribute("aria-valuemax", "255"); this.lightLevelButton.setAttribute("aria-orientation", "vertical"); @@ -856,7 +969,6 @@ path.sim-board { svg.setGradientValue(this.lightLevelGradient, Math.min(100, Math.max(0, Math.floor(lv * 100 / 255))) + '%') this.lightLevelText.textContent = lv.toString(); this.lightLevelButton.setAttribute("aria-valuenow", lv.toString()); - accessibility.setLiveContent(lv.toString()); } findParentElement() { @@ -886,9 +998,10 @@ path.sim-board { el.style.perspective = "30em"; // don't display acc data when AB is on, v2 is on or soundLevel is on + const soundLevelVisible = this.soundLevel.style.visibility == "visible"; if (state.buttonPairState.usesButtonAB || this.v2Circle - || this.soundLevel) { + || soundLevelVisible) { if (this.accTextX) this.accTextX.textContent = ""; if (this.accTextY) this.accTextY.textContent = ""; if (this.accTextZ) this.accTextZ.textContent = ""; @@ -896,21 +1009,21 @@ path.sim-board { // update text if (acc.flags & AccelerometerFlag.X) { if (!this.accTextX) { - this.accTextX = svg.child(this.g, "text", { x: 365, y: 260, class: "sim-text" }) as SVGTextElement; + this.accTextX = svg.child(this.g, "text", { x: 365, y: 260, class: "sim-text", "aria-hidden": true }) as SVGTextElement; this.accTextX.textContent = ""; } this.accTextX.textContent = `ax:${x}`; } if (acc.flags & AccelerometerFlag.Y) { if (!this.accTextY) { - this.accTextY = svg.child(this.g, "text", { x: 365, y: 285, class: "sim-text" }) as SVGTextElement; + this.accTextY = svg.child(this.g, "text", { x: 365, y: 285, class: "sim-text", "aria-hidden": true }) as SVGTextElement; this.accTextY.textContent = ""; } this.accTextY.textContent = `ay:${-y}`; } if (acc.flags & AccelerometerFlag.Z) { if (!this.accTextZ) { - this.accTextZ = svg.child(this.g, "text", { x: 365, y: 310, class: "sim-text" }) as SVGTextElement; + this.accTextZ = svg.child(this.g, "text", { x: 365, y: 310, class: "sim-text", "aria-hidden": true }) as SVGTextElement; this.accTextZ.textContent = ""; } this.accTextZ.textContent = `az:${z}`; @@ -929,7 +1042,9 @@ path.sim-board { "y": "0px", "width": MB_WIDTH + "px", "height": MB_HEIGHT + "px", - "fill": "rgba(0,0,0,0)" + "fill": "rgba(0,0,0,0)", + // Allows screen reader users to interact with board properly. + "role": "application" }); this.style = svg.child(this.element, "style", {}); this.style.textContent = MB_STYLE + (this.props.theme.highContrast ? MB_HIGHCONTRAST : ""); @@ -984,10 +1099,86 @@ path.sim-board { } } + // Order of construction affects tab ordering + this.buildLightLevelElement(); + this.buildAntennaElement(); + this.buildHeadElement(); + this.buildThermometerElement(); + this.buildSoundLevel(); + this.buildShakeElement(); + this.buildButtonElements(); + this.buildPinElements(); + } + + private buildAntennaElement() { + this.antenna = svg.child(this.g, "g", { class: "sim-antenna-outer" }); + + const ayt = 10; + const ayb = 40; + const antennaWidth = ANTENNA_WAVE_PERIOD_X * ANTENNA_WAVE_COUNT; + const borderOffset = 3; + + svg.child(this.antenna, "rect", { + x: ANTENNA_X - borderOffset, + y: ayt - borderOffset, + width: antennaWidth + 2 * borderOffset, + height: ayb - ayt + 2 * borderOffset, + fill: "transparent", + rx: 2 + }); + + let ax = ANTENNA_X; + const dax = ANTENNA_WAVE_PERIOD_X; + svg.child(this.antenna, "polyline", { class: "sim-antenna", points: `${ax},${ayb} ${ax},${ayt} ${ax += dax},${ayt} ${ax},${ayb} ${ax += dax},${ayb} ${ax},${ayt} ${ax += dax},${ayt} ${ax},${ayb} ${ax += dax},${ayb} ${ax},${ayt} ${ax += dax},${ayt}` }); + this.antenna.style.visibility = "hidden"; + } + + private buildSoundLevel() { + let gid = "gradient-soundlevel"; + this.soundLevelGradient = svg.linearGradient(this.defs, gid); + this.soundLevel = svg.child(this.g, "rect", { + class: "sim-thermometer no-drag", + x: 360, + y: 110, + width: 20, + height: 160, + rx: 5, ry: 5, + fill: `url(#${gid})` + }); + this.soundLevel.style.visibility = "hidden"; + } + + private buildThermometerElement() { + let gid = "gradient-thermometer"; + this.thermometerGradient = svg.linearGradient(this.defs, gid); + this.thermometer = svg.child(this.g, "rect", { + class: "sim-thermometer no-drag", + x: 120, + y: 110, + width: 20, + height: 160, + rx: 5, ry: 5, + fill: `url(#${gid})` + }); + this.thermometer.style.visibility = "hidden"; + } + + private buildLightLevelElement() { + let gid = "gradient-light-level"; + this.lightLevelGradient = svg.linearGradient(this.defs, gid); + this.lightLevelButton = svg.child(this.g, "circle", { + cx: `50px`, cy: `${LIGHT_LEVEL_BUTTON_POSITION_Y}px`, r: `${LIGHT_LEVEL_BUTTON_RADIUS}px`, + class: 'sim-light-level-button no-drag', + fill: `url(#${gid})` + }) as SVGCircleElement; + this.lightLevelButton.style.visibility = "hidden"; + } + + private buildHeadElement() { // head this.head = svg.child(this.g, "g", { class: "sim-head" }); - svg.child(this.head, "circle", { cx: 258, cy: 75, r: 100, fill: "transparent" }) - this.headParts = svg.child(this.head, "g", { class: "sim-button-outer sim-button-group" }); + svg.child(this.head, "ellipse", { cx: 251, cy: 75, rx: 75, ry: 35, fill: "transparent" }) + this.headParts = svg.child(this.head, "g", {}); this.heads = [] // background this.heads.push(svg.path(this.headParts, "sim-button", "M 269.9 50.2 L 269.9 50.2 l -39.5 0 v 0 c -14.1 0.1 -24.6 10.7 -24.6 24.8 c 0 13.9 10.4 24.4 24.3 24.7 v 0 h 39.6 c 14.2 0 24.8 -10.6 24.8 -24.7 C 294.5 61 284 50.3 269.9 50.2 M 269.7 89.2")); @@ -995,37 +1186,71 @@ path.sim-board { this.heads.push(svg.path(this.headParts, "sim-theme", "M269.9,50.2L269.9,50.2l-39.5,0v0c-14.1,0.1-24.6,10.7-24.6,24.8c0,13.9,10.4,24.4,24.3,24.7v0h39.6c14.2,0,24.8-10.6,24.8-24.7C294.5,61,284,50.3,269.9,50.2 M269.7,89.2L269.7,89.2l-39.3,0c-7.7-0.1-14-6.4-14-14.2c0-7.8,6.4-14.2,14.2-14.2h39.1c7.8,0,14.2,6.4,14.2,14.2C283.9,82.9,277.5,89.2,269.7,89.2")); this.heads.push(svg.path(this.headParts, "sim-theme", "M230.6,69.7c-2.9,0-5.3,2.4-5.3,5.3c0,2.9,2.4,5.3,5.3,5.3c2.9,0,5.3-2.4,5.3-5.3C235.9,72.1,233.5,69.7,230.6,69.7")); this.heads.push(svg.path(this.headParts, "sim-theme", "M269.7,80.3c2.9,0,5.3-2.4,5.3-5.3c0-2.9-2.4-5.3-5.3-5.3c-2.9,0-5.3,2.4-5.3,5.3C264.4,77.9,266.8,80.3,269.7,80.3")); - this.headText = svg.child(this.g, "text", { x: 160, y: 60, class: "sim-text" }) + this.headText = svg.child(this.g, "text", { x: 160, y: 60, class: "sim-text", "aria-hidden": true }) + } + private buildPinElements() { // https://www.microbit.co.uk/device/pins + // The order of this.pins must match the edgeConnectorState.pins order. + // The draw order must match the desired tab order. To this end we + // create the drawlist in sim order and evaluate it in tab order. + // P0, P1, P2 - this.pins = [ + let drawList: (() => SVGElement)[] = [ "M16.5,341.2c0,0.4-0.1,0.9-0.1,1.3v60.7c4.1,1.7,8.6,2.7,12.9,2.7h34.4v-64.7c0,0,0-0.1,0-0.1c0-13-10.6-23.6-23.7-23.6C27.2,317.6,16.5,328.1,16.5,341.2z M21.2,341.6c0-10.7,8.7-19.3,19.3-19.3c10.7,0,19.3,8.7,19.3,19.3c0,10.7-8.6,19.3-19.3,19.3C29.9,360.9,21.2,352.2,21.2,341.6z", "M139.1,317.3c-12.8,0-22.1,10.3-23.1,23.1V406h46.2v-65.6C162.2,327.7,151.9,317.3,139.1,317.3zM139.3,360.1c-10.7,0-19.3-8.6-19.3-19.3c0-10.7,8.6-19.3,19.3-19.3c10.7,0,19.3,8.7,19.3,19.3C158.6,351.5,150,360.1,139.3,360.1z", "M249,317.3c-12.8,0-22.1,10.3-23.1,23.1V406h46.2v-65.6C272.1,327.7,261.8,317.3,249,317.3z M249.4,360.1c-10.7,0-19.3-8.6-19.3-19.3c0-10.7,8.6-19.3,19.3-19.3c10.7,0,19.3,8.7,19.3,19.3C268.7,351.5,260.1,360.1,249.4,360.1z" - ].map((p, pi) => svg.path(this.g, "sim-pin sim-pin-touch", p)); + ].map((p) => () => svg.path(this.g, "sim-pin sim-pin-touch", p)); // P3 - this.pins.push(svg.path(this.g, "sim-pin", "M0,357.7v19.2c0,10.8,6.2,20.2,14.4,25.2v-44.4H0z")); + drawList.push(() => svg.path(this.g, "sim-pin", "M0,357.7v19.2c0,10.8,6.2,20.2,14.4,25.2v-44.4H0z")); pins4onXs.forEach(x => { - this.pins.push(svg.child(this.g, "rect", { x: x, y: 356.7, width: 10, height: 50, class: "sim-pin" })); - }) - this.pins.push(svg.path(this.g, "sim-pin", "M483.6,402c8.2-5,14.4-14.4,14.4-25.1v-19.2h-14.4V402z")); - this.pins.push(svg.path(this.g, "sim-pin", "M359.9,317.3c-12.8,0-22.1,10.3-23.1,23.1V406H383v-65.6C383,327.7,372.7,317.3,359.9,317.3z M360,360.1c-10.7,0-19.3-8.6-19.3-19.3c0-10.7,8.6-19.3,19.3-19.3c10.7,0,19.3,8.7,19.3,19.3C379.3,351.5,370.7,360.1,360,360.1z")); - this.pins.push(svg.path(this.g, "sim-pin", "M458,317.6c-13,0-23.6,10.6-23.6,23.6c0,0,0,0.1,0,0.1h0V406H469c4.3,0,8.4-1,12.6-2.7v-60.7c0-0.4,0-0.9,0-1.3C481.6,328.1,471,317.6,458,317.6z M457.8,360.9c-10.7,0-19.3-8.6-19.3-19.3c0-10.7,8.6-19.3,19.3-19.3c10.7,0,19.3,8.7,19.3,19.3C477.1,352.2,468.4,360.9,457.8,360.9z")); + drawList.push(() => svg.child(this.g, "rect", { x: x, y: 356.7, width: 10, height: 50, class: "sim-pin" })); + }); - this.pins.forEach((p, i) => svg.hydrate(p, { title: pinTitles[i] })); + drawList.push(() => svg.path(this.g, "sim-pin", "M483.6,402c8.2-5,14.4-14.4,14.4-25.1v-19.2h-14.4V402z")); + drawList.push(() => svg.path(this.g, "sim-pin", "M359.9,317.3c-12.8,0-22.1,10.3-23.1,23.1V406H383v-65.6C383,327.7,372.7,317.3,359.9,317.3z M360,360.1c-10.7,0-19.3-8.6-19.3-19.3c0-10.7,8.6-19.3,19.3-19.3c10.7,0,19.3,8.7,19.3,19.3C379.3,351.5,370.7,360.1,360,360.1z")); + drawList.push(() => svg.path(this.g, "sim-pin", "M458,317.6c-13,0-23.6,10.6-23.6,23.6c0,0,0,0.1,0,0.1h0V406H469c4.3,0,8.4-1,12.6-2.7v-60.7c0-0.4,0-0.9,0-1.3C481.6,328.1,471,317.6,458,317.6z M457.8,360.9c-10.7,0-19.3-8.6-19.3-19.3c0-10.7,8.6-19.3,19.3-19.3c10.7,0,19.3,8.7,19.3,19.3C477.1,352.2,468.4,360.9,457.8,360.9z")); + + this.pins = pinDrawOrder.reduce((pins, pinName) => { + const simPinIndex = pinNames.indexOf(pinName); + const newPin = drawList[simPinIndex](); + svg.hydrate(newPin, { title: pinTitles[simPinIndex].ariaLabel }); + pins[simPinIndex] = newPin; + return pins; + }, new Array(pinDrawOrder.length)); this.pinGradients = this.pins.map((pin, i) => { let gid = "gradient-pin-" + i let lg = svg.linearGradient(this.defs, gid) pin.setAttribute("fill", `url(#${gid})`); return lg; - }) + }); - this.pinTexts = [67, 165, 275].map(x => svg.child(this.g, "text", { class: "sim-text-pin", x: x, y: 345 })); + this.pinTexts = [67, 165, 275].map(x => svg.child(this.g, "text", { class: "sim-text-pin", x: x, y: 345, "aria-hidden": true })); + svg.path(this.g, "sim-label", "M35.7,376.4c0-2.8,2.1-5.1,5.5-5.1c3.3,0,5.5,2.4,5.5,5.1v4.7c0,2.8-2.2,5.1-5.5,5.1c-3.3,0-5.5-2.4-5.5-5.1V376.4zM43.3,376.4c0-1.3-0.8-2.3-2.2-2.3c-1.3,0-2.1,1.1-2.1,2.3v4.7c0,1.2,0.8,2.3,2.1,2.3c1.3,0,2.2-1.1,2.2-2.3V376.4z"); + svg.path(this.g, "sim-label", "M136.2,374.1c2.8,0,3.4-0.8,3.4-2.5h2.9v14.3h-3.4v-9.5h-3V374.1z"); + svg.path(this.g, "sim-label", "M248.6,378.5c1.7-1,3-1.7,3-3.1c0-1.1-0.7-1.6-1.6-1.6c-1,0-1.8,0.6-1.8,2.1h-3.3c0-2.6,1.8-4.6,5.1-4.6c2.6,0,4.9,1.3,4.9,4.3c0,2.4-2.3,3.9-3.8,4.7c-2,1.3-2.5,1.8-2.5,2.9h6.1v2.7h-10C244.8,381.2,246.4,379.9,248.6,378.5z"); + svg.path(this.g, "sim-label", "M352.1,381.1c0,1.6,0.9,2.5,2.2,2.5c1.2,0,1.9-0.9,1.9-1.9c0-1.2-0.6-2-2.1-2h-1.3v-2.6h1.3c1.5,0,1.9-0.7,1.9-1.8c0-1.1-0.7-1.6-1.6-1.6c-1.4,0-1.8,0.8-1.8,2.1h-3.3c0-2.4,1.5-4.6,5.1-4.6c2.6,0,5,1.3,5,4c0,1.6-1,2.8-2.1,3.2c1.3,0.5,2.3,1.6,2.3,3.5c0,2.7-2.4,4.3-5.2,4.3c-3.5,0-5.5-2.1-5.5-5.1H352.1z") + svg.path(this.g, "sim-label", "M368.5,385.9h-3.1l-5.1-14.3h3.5l3.1,10.1l3.1-10.1h3.6L368.5,385.9z") + svg.path(this.g, "sim-label", "M444.4,378.3h7.4v2.5h-1.5c-0.6,3.3-3,5.5-7.1,5.5c-4.8,0-7.5-3.5-7.5-7.5c0-3.9,2.8-7.5,7.5-7.5c3.8,0,6.4,2.3,6.6,5h-3.5c-0.2-1.1-1.4-2.2-3.1-2.2c-2.7,0-4.1,2.3-4.1,4.7c0,2.5,1.4,4.7,4.4,4.7c2,0,3.2-1.2,3.4-2.7h-2.5V378.3z") + svg.path(this.g, "sim-label", "M461.4,380.9v-9.3h3.3v14.3h-3.5l-5.2-9.2v9.2h-3.3v-14.3h3.5L461.4,380.9z") + svg.path(this.g, "sim-label", "M472.7,371.6c4.8,0,7.5,3.5,7.5,7.2s-2.7,7.2-7.5,7.2h-5.3v-14.3H472.7z M470.8,374.4v8.6h1.8c2.7,0,4.2-2.1,4.2-4.3s-1.6-4.3-4.2-4.3H470.8z") + } + + private buildShakeElement() { + this.shakeButton = svg.child(this.g, "circle", { + cx: 404, + cy: 115, + r: 12, + class: "sim-shake", + }) as SVGCircleElement; + this.shakeButton.style.visibility = "hidden"; + } + + private buildButtonElements() { this.buttonsOuter = []; this.buttons = []; const outerBtn = (left: number, top: number, label: string) => { @@ -1054,18 +1279,8 @@ path.sim-board { (this.buttons[2]).style.visibility = "hidden"; this.buttons.forEach(btn => svg.hydrate(btn, { fill: "#111" })); - svg.path(this.g, "sim-label", "M35.7,376.4c0-2.8,2.1-5.1,5.5-5.1c3.3,0,5.5,2.4,5.5,5.1v4.7c0,2.8-2.2,5.1-5.5,5.1c-3.3,0-5.5-2.4-5.5-5.1V376.4zM43.3,376.4c0-1.3-0.8-2.3-2.2-2.3c-1.3,0-2.1,1.1-2.1,2.3v4.7c0,1.2,0.8,2.3,2.1,2.3c1.3,0,2.2-1.1,2.2-2.3V376.4z"); - svg.path(this.g, "sim-label", "M136.2,374.1c2.8,0,3.4-0.8,3.4-2.5h2.9v14.3h-3.4v-9.5h-3V374.1z"); - svg.path(this.g, "sim-label", "M248.6,378.5c1.7-1,3-1.7,3-3.1c0-1.1-0.7-1.6-1.6-1.6c-1,0-1.8,0.6-1.8,2.1h-3.3c0-2.6,1.8-4.6,5.1-4.6c2.6,0,4.9,1.3,4.9,4.3c0,2.4-2.3,3.9-3.8,4.7c-2,1.3-2.5,1.8-2.5,2.9h6.1v2.7h-10C244.8,381.2,246.4,379.9,248.6,378.5z"); - svg.path(this.g, "sim-button-label", "M48.1,270.9l-0.6-1.7h-5.1l-0.6,1.7h-3.5l5.1-14.3h3.1l5.2,14.3H48.1z M45,260.7l-1.8,5.9h3.5L45,260.7z"); svg.path(this.g, "sim-button-label", "M449.1,135.8h5.9c3.9,0,4.7,2.4,4.7,3.9c0,1.8-1.4,2.9-2.5,3.2c0.9,0,2.6,1.1,2.6,3.3c0,1.5-0.8,4-4.7,4h-6V135.8zM454.4,141.7c1.6,0,2-1,2-1.7c0-0.6-0.3-1.7-2-1.7h-2v3.4H454.4z M452.4,144.1v3.5h2.1c1.6,0,2-1,2-1.8c0-0.7-0.4-1.8-2-1.8H452.4z") - - svg.path(this.g, "sim-label", "M352.1,381.1c0,1.6,0.9,2.5,2.2,2.5c1.2,0,1.9-0.9,1.9-1.9c0-1.2-0.6-2-2.1-2h-1.3v-2.6h1.3c1.5,0,1.9-0.7,1.9-1.8c0-1.1-0.7-1.6-1.6-1.6c-1.4,0-1.8,0.8-1.8,2.1h-3.3c0-2.4,1.5-4.6,5.1-4.6c2.6,0,5,1.3,5,4c0,1.6-1,2.8-2.1,3.2c1.3,0.5,2.3,1.6,2.3,3.5c0,2.7-2.4,4.3-5.2,4.3c-3.5,0-5.5-2.1-5.5-5.1H352.1z") - svg.path(this.g, "sim-label", "M368.5,385.9h-3.1l-5.1-14.3h3.5l3.1,10.1l3.1-10.1h3.6L368.5,385.9z") - svg.path(this.g, "sim-label", "M444.4,378.3h7.4v2.5h-1.5c-0.6,3.3-3,5.5-7.1,5.5c-4.8,0-7.5-3.5-7.5-7.5c0-3.9,2.8-7.5,7.5-7.5c3.8,0,6.4,2.3,6.6,5h-3.5c-0.2-1.1-1.4-2.2-3.1-2.2c-2.7,0-4.1,2.3-4.1,4.7c0,2.5,1.4,4.7,4.4,4.7c2,0,3.2-1.2,3.4-2.7h-2.5V378.3z") - svg.path(this.g, "sim-label", "M461.4,380.9v-9.3h3.3v14.3h-3.5l-5.2-9.2v9.2h-3.3v-14.3h3.5L461.4,380.9z") - svg.path(this.g, "sim-label", "M472.7,371.6c4.8,0,7.5,3.5,7.5,7.2s-2.7,7.2-7.5,7.2h-5.3v-14.3H472.7z M470.8,374.4v8.6h1.8c2.7,0,4.2-2.1,4.2-4.3s-1.6-4.3-4.2-4.3H470.8z") } private updateHardwareVersion() { @@ -1087,10 +1302,10 @@ path.sim-board { } // display v2 indicator - const title = pxsim.localization.lf("micro:bit V2 needed") + const title = pxsim.localization.lf("micro:bit v2 needed") this.v2Circle = svg.child(this.g, "circle", { r: 21, title: title }); svg.fill(this.v2Circle, "white"); - this.v2Text = svg.child(this.g, "text", { class: "sim-text", title: title }); + this.v2Text = svg.child(this.g, "text", { class: "sim-text", title: title, "aria-hidden": true }); this.v2Text.textContent = "V2"; svg.fill(this.v2Text, "black"); this.v2Text.style.fontWeight = "700"; @@ -1108,18 +1323,20 @@ path.sim-board { // outline this.pkg.setAttribute("d", "M 498 31.9 C 498 14.3 483.7 0 466.1 0 H 31.9 C 14.3 0 0 14.3 0 31.9 v 342.2 C -1 399 21 405 23 406 c 0 0 -1 -9 8 -8 l 18 0 c 0 0 9 -1 8 8 h 7 h 50 h 7 c 0 0 -1 -9 8 -8 l 18 0 c 0 0 9 -1 8 8 h 7 h 63 h 7 c 0 0 -1 -9 8 -8 l 18 0 c 0 0 9 -1 8 8 h 7 h 64 h 7 c 0 0 -1 -9 8 -8 l 18 0 c 0 0 9 -1 8 8 h 7 h 51 h 5 c 0 0 -1 -9 8 -8 l 18 0 c 0 0 9 -1 8 8 h 0 c 9 0 23 -17 23 -31 V 31.9 z M 14.3 206.7 c -2.7 0 -4.8 -2.2 -4.8 -4.8 c 0 -2.7 2.2 -4.8 4.8 -4.8 c 2.7 0 4.8 2.2 4.8 4.8 C 19.2 204.6 17 206.7 14.3 206.7 z M 486.2 206.7 c -2.7 0 -4.8 -2.2 -4.8 -4.8 c 0 -2.72 0.2 -4.8 4.8 -4.8 c 2.7 0 4.8 2.2 4.8 4.8 C 491 204.6 488.8 206.7 486.2 206.7 z") - const headTitle = pxsim.localization.lf("logo touch (micro:bit V2 needed)") + const headTitle = pxsim.localization.lf("logo touch (micro:bit v2 needed)") accessibility.makeFocusable(this.headParts); accessibility.setAria(this.headParts, "button", headTitle); + this.headParts.setAttribute("class", "sim-button-outer sim-button-group") + this.attachButtonEvents(this.board.logoTouch, this.headParts, this.headParts); + document.body.addEventListener(pointerEvents.down[0], this.moveHeadingOnClick); // microphone led - const microphoneTitle = pxsim.localization.lf("microphone (microbit:v2 needed)") + const microphoneTitle = pxsim.localization.lf("microphone (micro:bit v2 needed)") const microg = svg.child(this.g, "g", { title: microphoneTitle }) this.microphoneLed = svg.path(microg, "sim-led sim-mic", "M 352.852 71 C 351.315 71 350.07 72.248 350.07 73.784 V 79.056 C 350.07 80.594 351.316 81.838 352.852 81.838 C 354.387 81.838 355.634 80.593 355.634 79.056 V 73.784 C 355.634 72.248 354.387 71 352.852 71 Z M 346.743 79.981 C 346.743 82.84 348.853 85.062 351.501 85.658 V 87.095 H 348.448 V 89.329 H 357.366 V 87.095 H 354.306 V 85.658 C 356.954 85.064 359.071 82.842 359.071 79.981 H 357.057 C 357.057 82.174 355.168 83.81 352.905 83.81 C 350.64 83.81 348.757 82.173 348.757 79.981 Z"); svg.fills([this.microphoneLed], this.props.theme.ledOff); // ring - const microhole = svg.child(this.g, "circle", { cx: 336, cy: 86, r: 3, stroke: "gold", strokeWidth: "1px" }) - svg.title(microhole, pxsim.localization.lf("microphone (microbit:v2 needed)")) + svg.child(this.g, "circle", { cx: 336, cy: 86, r: 3, stroke: "gold", strokeWidth: "1px" }) this.updateMicrophone(); this.updateTheme(); @@ -1152,6 +1369,7 @@ path.sim-board { this.attachPinsTouchEvents(); this.attachABEvents(); this.attachAPlusBEvents(); + this.attachKeyboardEvents(); } private attachIFrameEvents() { @@ -1168,24 +1386,47 @@ path.sim-board { } private attachAccelerometerEvents() { - let tiltDecayer = 0; - this.element.addEventListener(pointerEvents.move, (ev: MouseEvent) => { - const state = this.board; - if (!state.accelerometerState.accelerometer.isActive) return; + let tiltDecayer: any = undefined; + const state = this.board; - if (tiltDecayer) { - clearInterval(tiltDecayer); - tiltDecayer = 0; + const startTiltDecay = () => { + if (!tiltDecayer) { + const doDecay = () => { + let accx = state.accelerometerState.accelerometer.getX(MicroBitCoordinateSystem.RAW); + accx = Math.floor(Math.abs(accx) * 0.85) * (accx > 0 ? 1 : -1); + let accy = state.accelerometerState.accelerometer.getY(MicroBitCoordinateSystem.RAW); + accy = Math.floor(Math.abs(accy) * 0.85) * (accy > 0 ? 1 : -1); + let accz = -Math.sqrt(Math.max(0, 1023 * 1023 - accx * accx - accy * accy)); + if (Math.abs(accx) <= 24 && Math.abs(accy) <= 24) { + cancelAnimationFrame(tiltDecayer); + tiltDecayer = 0; + accx = 0; + accy = 0; + accz = -1023; + } + else { + tiltDecayer = requestAnimationFrame(doDecay); + } + state.accelerometerState.accelerometer.update(accx, accy, accz); + this.updateTilt(); + } + tiltDecayer = requestAnimationFrame(doDecay) } + } - const bbox = this.element.getBoundingClientRect(); + const handleMove = (xPos: number, yPos: number, boardWidth: number, boardHeight: number) => { + if (yPos > boardHeight || xPos < 0 || xPos > boardWidth) { + startTiltDecay(); + return; + } - // ev.clientX and ev.clientY are not defined on mobile iOS - const xPos = ev.clientX != null ? ev.clientX : ev.pageX; - const yPos = ev.clientY != null ? ev.clientY : ev.pageY; + if (tiltDecayer) { + cancelAnimationFrame(tiltDecayer); + tiltDecayer = undefined; + } - const ax = (xPos - bbox.width / 2) / (bbox.width / 3); - const ay = (yPos - bbox.height / 2) / (bbox.height / 3); + const ax = (xPos - boardWidth / 2) / (boardWidth / 3); + const ay = (yPos - boardHeight / 2) / (boardHeight / 3); const x = - Math.max(- 1023, Math.min(1023, Math.floor(ax * 1023))); const y = - Math.max(- 1023, Math.min(1023, Math.floor(ay * 1023))); @@ -1194,29 +1435,92 @@ path.sim-board { state.accelerometerState.accelerometer.update(x, y, z); this.updateTilt(); + } + + this.bindEvent(document, pointerEvents.move, (ev: MouseEvent) => { + if (!state.accelerometerState.accelerometer.isActive) return; + + const boardElement = this.element as unknown as HTMLElement; + const parentSvg = this.findParentElement(); + + const xPos = ev.clientX != null ? ev.clientX : ev.pageX; + const yPos = ev.clientY != null ? ev.clientY : ev.pageY; + + // The outermost SVG has a transform applied to it to create the tilt + // effect. In order to give us a constant bounding box to work with, + // we want to calculate the pre-transform bounds of the board element + // we can do this by comparing the aspect ratio of the page to the + // viewbox of the board SVG, since it should always be maximized within + // the page. + const pageBounds = document.body.getBoundingClientRect(); + + if (parentSvg && parentSvg !== this.element) { + // If we are embedded in another SVG (e.g. the breadboard is present), + // we need to do some extra work to find the bounding box of the board + // element within the parent SVG. + const parentViewBoxWidth = parentSvg.viewBox.baseVal.width; + const parentViewBoxHeight = parentSvg.viewBox.baseVal.height; + + const aspectRatio = parentViewBoxWidth / parentViewBoxHeight; + + let parentWidth: number; + let parentHeight: number; + + if (pageBounds.width / pageBounds.height > aspectRatio) { + parentHeight = pageBounds.height; + parentWidth = parentHeight * aspectRatio; + } + else { + parentWidth = pageBounds.width; + parentHeight = parentWidth / aspectRatio; + } + + const parentLeft = pageBounds.left + (pageBounds.width - parentWidth) / 2; + const parentTop = pageBounds.top + (pageBounds.height - parentHeight) / 2; + + + const boardWidth = parseFloat(boardElement.getAttribute("width")!); + const boardHeight = parseFloat(boardElement.getAttribute("height")!); + const boardLeft = parseFloat(boardElement.getAttribute("x")!); + const boardTop = parseFloat(boardElement.getAttribute("y")!); + + const boardPixelLeft = parentLeft + (boardLeft / parentViewBoxWidth) * parentWidth; + const boardPixelTop = parentTop + (boardTop / parentViewBoxHeight) * parentHeight; + + const boardPixelWidth = (boardWidth / parentViewBoxWidth) * parentWidth; + const boardPixelHeight = (boardHeight / parentViewBoxHeight) * parentHeight; + handleMove(xPos - boardPixelLeft, yPos - boardPixelTop, boardPixelWidth, boardPixelHeight); + } + else { + const boardViewboxWidth = this.element.viewBox.baseVal.width; + const boardViewboxHeight = this.element.viewBox.baseVal.height; + + const aspectRatio = boardViewboxWidth / boardViewboxHeight; + + let boardWidth: number; + let boardHeight: number; + + if (pageBounds.width / pageBounds.height > aspectRatio) { + boardHeight = pageBounds.height; + boardWidth = boardHeight * aspectRatio; + } + else { + boardWidth = pageBounds.width; + boardHeight = boardWidth / aspectRatio; + } + + const boardLeft = pageBounds.left + (pageBounds.width - boardWidth) / 2; + const boardTop = pageBounds.top + (pageBounds.height - boardHeight) / 2; + + handleMove(xPos - boardLeft, yPos - boardTop, boardWidth, boardHeight); + } }, false); - this.element.addEventListener(pointerEvents.leave, (ev: MouseEvent) => { + + this.bindEvent(document, pointerEvents.leave, (ev: MouseEvent) => { let state = this.board; if (!state.accelerometerState.accelerometer.isActive) return; - if (!tiltDecayer) { - tiltDecayer = setInterval(() => { - let accx = state.accelerometerState.accelerometer.getX(MicroBitCoordinateSystem.RAW); - accx = Math.floor(Math.abs(accx) * 0.85) * (accx > 0 ? 1 : -1); - let accy = state.accelerometerState.accelerometer.getY(MicroBitCoordinateSystem.RAW); - accy = Math.floor(Math.abs(accy) * 0.85) * (accy > 0 ? 1 : -1); - let accz = -Math.sqrt(Math.max(0, 1023 * 1023 - accx * accx - accy * accy)); - if (Math.abs(accx) <= 24 && Math.abs(accy) <= 24) { - clearInterval(tiltDecayer); - tiltDecayer = 0; - accx = 0; - accy = 0; - accz = -1023; - } - state.accelerometerState.accelerometer.update(accx, accy, accz); - this.updateTilt(); - }, 50) - } + startTiltDecay(); }, false); } @@ -1230,10 +1534,11 @@ path.sim-board { let state = this.board; let pin = state.edgeConnectorState.pins[index]; let svgpin = this.pins[index]; - if (pin.mode & PinFlags.Input) { + if (pin.mode & PinFlags.Input && !(pin.mode & PinFlags.Touch)) { let cursor = svg.cursorPoint(pt, this.element, ev); - let v = (400 - cursor.y) / 40 * 1023 - pin.value = Math.max(0, Math.min(1023, Math.floor(v))); + let maxValue = pin.mode & PinFlags.Analog ? 1023 : 1; + let v = (400 - cursor.y) / 40 * maxValue; + pin.value = Math.max(0, Math.min(maxValue, Math.floor(v))); } this.updatePin(pin, index); }, @@ -1243,10 +1548,11 @@ path.sim-board { let pin = state.edgeConnectorState.pins[index]; let svgpin = this.pins[index]; U.addClass(svgpin, "touched"); - if (pin.mode & PinFlags.Input) { + if (pin.mode & PinFlags.Input && !(pin.mode & PinFlags.Touch)) { let cursor = svg.cursorPoint(pt, this.element, ev); - let v = (400 - cursor.y) / 40 * 1023 - pin.value = Math.max(0, Math.min(1023, Math.floor(v))); + let maxValue = pin.mode & PinFlags.Analog ? 1023 : 1; + let v = (400 - cursor.y) / 40 * maxValue; + pin.value = Math.max(0, Math.min(maxValue, Math.floor(v))); } this.updatePin(pin, index); }, @@ -1261,21 +1567,11 @@ path.sim-board { }, // keydown (ev: KeyboardEvent) => { - let charCode = (typeof ev.which == "number") ? ev.which : ev.keyCode - let state = this.board; - let pin = state.edgeConnectorState.pins[index]; - - if (charCode === 40 || charCode === 37) { // Down/Left arrow - pin.value -= 10; - if (pin.value < 0) { - pin.value = 1023; - } - this.updatePin(pin, index); - } else if (charCode === 38 || charCode === 39) { // Up/Right arrow - pin.value += 10; - if (pin.value > 1023) { - pin.value = 0; - } + const state = this.board; + const pin = state.edgeConnectorState.pins[index]; + const value = pinKeyHandler(ev, pin.value, 0, pin.mode & PinFlags.Analog ? 1023 : 1, pin.mode); + if (value !== undefined) { + pin.value = value; this.updatePin(pin, index); } }); @@ -1309,64 +1605,86 @@ path.sim-board { this.board.bus.queue(state.edgeConnectorState.pins[index].id, DAL.MICROBIT_BUTTON_EVT_CLICK); pressedTime = undefined; }) - accessibility.enableKeyboardInteraction(btn, undefined, () => { - let state = this.board; - this.board.bus.queue(state.edgeConnectorState.pins[index].id, DAL.MICROBIT_BUTTON_EVT_DOWN); - this.board.bus.queue(state.edgeConnectorState.pins[index].id, DAL.MICROBIT_BUTTON_EVT_UP); - this.board.bus.queue(state.edgeConnectorState.pins[index].id, DAL.MICROBIT_BUTTON_EVT_CLICK); - }); + accessibility.enableKeyboardInteraction(btn, + () => { // keydown + let state = this.board; + state.edgeConnectorState.pins[index].touched = true; + let svgpin = this.pins[index]; + U.addClass(svgpin, "touched"); + this.updatePin(state.edgeConnectorState.pins[index], index); + this.board.bus.queue(state.edgeConnectorState.pins[index].id, DAL.MICROBIT_BUTTON_EVT_DOWN); + }, + () => { // keyup + let state = this.board; + state.edgeConnectorState.pins[index].touched = false; + let svgpin = this.pins[index]; + U.removeClass(svgpin, "touched"); + this.updatePin(state.edgeConnectorState.pins[index], index); + this.board.bus.queue(state.edgeConnectorState.pins[index].id, DAL.MICROBIT_BUTTON_EVT_UP); + this.board.bus.queue(state.edgeConnectorState.pins[index].id, DAL.MICROBIT_BUTTON_EVT_CLICK); + } + ); }) } private attachABEvents() { const bpState = this.board.buttonPairState; - const stateButtons: Button[] = [bpState.aBtn, bpState.bBtn, this.board.logoTouch]; - const elButtonOuters = this.buttonsOuter.slice(0, 2).concat(this.headParts); - const elButtons = this.buttons.slice(0, 2).concat(this.headParts); + const stateButtons: Button[] = [bpState.aBtn, bpState.bBtn]; + const elButtonOuters = this.buttonsOuter.slice(0, 2); + const elButtons = this.buttons.slice(0, 2); elButtonOuters.forEach((btn, index) => { - let pressedTime: number; - pointerEvents.down.forEach(evid => btn.addEventListener(evid, ev => { - console.log(`down ${stateButtons[index].id}`) - stateButtons[index].pressed = true; - svg.fill(elButtons[index], this.props.theme.buttonDown); - this.board.bus.queue(stateButtons[index].id, DAL.MICROBIT_BUTTON_EVT_DOWN); - pressedTime = runtime.runningTime() - })); - btn.addEventListener(pointerEvents.leave, ev => { - stateButtons[index].pressed = false; - svg.fill(elButtons[index], this.props.theme.buttonUp); - }) - btn.addEventListener(pointerEvents.up, ev => { - stateButtons[index].pressed = false; - svg.fill(elButtons[index], this.props.theme.buttonUp); - this.board.bus.queue(stateButtons[index].id, DAL.MICROBIT_BUTTON_EVT_UP); - const currentTime = runtime.runningTime() - if (currentTime - pressedTime > DAL.DEVICE_BUTTON_LONG_CLICK_TIME) - this.board.bus.queue(stateButtons[index].id, DAL.MICROBIT_BUTTON_EVT_LONG_CLICK); - else - this.board.bus.queue(stateButtons[index].id, DAL.MICROBIT_BUTTON_EVT_CLICK); - pressedTime = undefined; - }) - accessibility.enableKeyboardInteraction(btn, undefined, () => { - this.board.bus.queue(stateButtons[index].id, DAL.MICROBIT_BUTTON_EVT_DOWN); - this.board.bus.queue(stateButtons[index].id, DAL.MICROBIT_BUTTON_EVT_UP); - this.board.bus.queue(stateButtons[index].id, DAL.MICROBIT_BUTTON_EVT_CLICK); - }); + this.attachButtonEvents(stateButtons[index], btn, elButtons[index]); + }); + } + + attachButtonEvents(stateButton: Button, buttonOuter: SVGElement, elButton: SVGElement) { + let pressedTime: number; + pointerEvents.down.forEach(evid => buttonOuter.addEventListener(evid, ev => { + stateButton.pressed = true; + this.updateButtonPairs(); + this.board.bus.queue(stateButton.id, DAL.MICROBIT_BUTTON_EVT_DOWN); + pressedTime = runtime.runningTime() + })); + buttonOuter.addEventListener(pointerEvents.leave, ev => { + stateButton.pressed = false; + this.updateButtonPairs(); + svg.fill(elButton, this.props.theme.buttonUp); + }) + buttonOuter.addEventListener(pointerEvents.up, ev => { + stateButton.pressed = false; + this.updateButtonPairs(); + this.board.bus.queue(stateButton.id, DAL.MICROBIT_BUTTON_EVT_UP); + const currentTime = runtime.runningTime() + if (currentTime - pressedTime > DAL.DEVICE_BUTTON_LONG_CLICK_TIME) + this.board.bus.queue(stateButton.id, DAL.MICROBIT_BUTTON_EVT_LONG_CLICK); + else + this.board.bus.queue(stateButton.id, DAL.MICROBIT_BUTTON_EVT_CLICK); + pressedTime = undefined; }) + accessibility.enableKeyboardInteraction(buttonOuter, + () => { // keydown + stateButton.pressed = true; + this.updateButtonPairs(); + this.board.bus.queue(stateButton.id, DAL.MICROBIT_BUTTON_EVT_DOWN); + }, () => { // keyup + stateButton.pressed = false; + this.updateButtonPairs(); + this.board.bus.queue(stateButton.id, DAL.MICROBIT_BUTTON_EVT_UP); + this.board.bus.queue(stateButton.id, DAL.MICROBIT_BUTTON_EVT_CLICK); + }); } private attachAPlusBEvents() { const bpState = this.board.buttonPairState; + let pressedTime: number; // A+B pointerEvents.down.forEach(evid => this.buttonsOuter[2].addEventListener(evid, ev => { bpState.aBtn.pressed = true; bpState.bBtn.pressed = true; bpState.abBtn.pressed = true; - svg.fill(this.buttons[0], this.props.theme.buttonDown); - svg.fill(this.buttons[1], this.props.theme.buttonDown); - svg.fill(this.buttons[2], this.props.theme.buttonDown); + this.updateButtonPairs(); this.board.bus.queue(bpState.abBtn.id, DAL.MICROBIT_BUTTON_EVT_DOWN); pressedTime = runtime.runningTime() })); @@ -1374,17 +1692,13 @@ path.sim-board { bpState.aBtn.pressed = false; bpState.bBtn.pressed = false; bpState.abBtn.pressed = false; - svg.fill(this.buttons[0], this.props.theme.buttonUp); - svg.fill(this.buttons[1], this.props.theme.buttonUp); - svg.fill(this.buttons[2], this.props.theme.virtualButtonUp); + this.updateButtonPairs(); }) this.buttonsOuter[2].addEventListener(pointerEvents.up, ev => { bpState.aBtn.pressed = false; bpState.bBtn.pressed = false; bpState.abBtn.pressed = false; - svg.fill(this.buttons[0], this.props.theme.buttonUp); - svg.fill(this.buttons[1], this.props.theme.buttonUp); - svg.fill(this.buttons[2], this.props.theme.virtualButtonUp); + this.updateButtonPairs(); this.board.bus.queue(bpState.abBtn.id, DAL.MICROBIT_BUTTON_EVT_UP); const currentTime = runtime.runningTime() @@ -1393,12 +1707,96 @@ path.sim-board { else this.board.bus.queue(bpState.abBtn.id, DAL.MICROBIT_BUTTON_EVT_CLICK); pressedTime = undefined; - }) - accessibility.enableKeyboardInteraction(this.buttonsOuter[2], undefined, () => { - this.board.bus.queue(bpState.abBtn.id, DAL.MICROBIT_BUTTON_EVT_DOWN); - this.board.bus.queue(bpState.abBtn.id, DAL.MICROBIT_BUTTON_EVT_UP); - this.board.bus.queue(bpState.abBtn.id, DAL.MICROBIT_BUTTON_EVT_CLICK); }); + + accessibility.enableKeyboardInteraction(this.buttonsOuter[2], + () => { // keydown + bpState.aBtn.pressed = true; + bpState.bBtn.pressed = true; + bpState.abBtn.pressed = true; + this.updateButtonPairs(); + this.board.bus.queue(bpState.abBtn.id, DAL.MICROBIT_BUTTON_EVT_DOWN); + }, () => { // keyup + bpState.aBtn.pressed = false; + bpState.bBtn.pressed = false; + bpState.abBtn.pressed = false; + this.updateButtonPairs(); + this.board.bus.queue(bpState.abBtn.id, DAL.MICROBIT_BUTTON_EVT_UP); + this.board.bus.queue(bpState.abBtn.id, DAL.MICROBIT_BUTTON_EVT_CLICK); + } + ); + } + + private attachKeyboardEvents() { + accessibility.postKeyboardEvent(); + } + + private bindEvent(element: Element | Document, eventName: string, handler: (e: Event) => void, ...rest: any[]) { + element.addEventListener(eventName, handler, ...rest); + this.bindings.push({ element, event: eventName, handler }); + } + } + + const isHandledKey = (key: string) => { + return ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "PageUp", "PageDown", "Home", "End"].includes(key); + } + + const getSliderStepValue = (min: number, max: number) => { + const range = max - min; + // Assumes slider values are always integers. + return Math.max(1, Math.floor(range / 10)); + } + + const commonKeyHandler = (e: KeyboardEvent, currentValue: number, min: number, max: number): number | undefined => { + const key = e.key; + if (isHandledKey(key)) { + e.preventDefault(); + } + switch (key) { + case "ArrowDown": + case "ArrowLeft": { + return Math.max(min, currentValue - 1) + } + case "ArrowUp": + case "ArrowRight": { + return Math.min(max, currentValue + 1) + } + case "Home": { + return min; + } + case "End": { + return max; + } + case "PageDown": { + const step = getSliderStepValue(min, max); + const value = currentValue - step; + return Math.max(min, value) + } + case "PageUp": { + const step = getSliderStepValue(min, max); + const value = currentValue + step; + return Math.min(max, value) + } + } + return undefined; + } + + const pinKeyHandler = (e: KeyboardEvent, currentValue: number, min: number, max: number, pinMode: PinFlags): number | undefined => { + const key = e.key; + if (isHandledKey(key)) { + if (!(pinMode & PinFlags.Input)) { + e.preventDefault(); + accessibility.setLiveContent(pxsim.localization.lf("This pin is read-only")); + return undefined; + } + if (pinMode & PinFlags.Touch) { + // The pin is in touch mode and has button markup, not a slider. + e.preventDefault(); + return undefined; + } } + // The pin value for a digital pin may be higher than 1 depending on how its value was set. + const currentValueClamped = Math.min(max, currentValue); + return commonKeyHandler(e, currentValueClamped, min, max); } } diff --git a/sim/visuals/mute.ts b/sim/visuals/mute.ts new file mode 100644 index 00000000000..9ed2774c51c --- /dev/null +++ b/sim/visuals/mute.ts @@ -0,0 +1,69 @@ +namespace pxsim { + const icon = ``; + + // We only need to unmute from within the iframe once + let hasUnmuted = false; + + export function createMuteButton() { + const el = document.createElement("div"); + el.setAttribute("id", "safari-mute-button-outer"); + el.innerHTML = ` + + `; + + const button = el.firstElementChild as HTMLButtonElement; + button.setAttribute("title", pxsim.localization.lf("Unmute simulator")); + + button.addEventListener("click", () => { + AudioContextManager.mute(false); + setParentMuteState("unmuted"); + button.remove(); + hasUnmuted = true; + }); + + return el; + } + + export function shouldShowMute() { + return isSafari() && !hasUnmuted; + } + + // Everything below is taken from browserutils in pxt + + export function hasNavigator(): boolean { + return typeof navigator !== "undefined"; + } + + //Microsoft Edge lies about its user agent and claims to be Chrome, but Microsoft Edge/Version + //is always at the end + export function isEdge(): boolean { + return hasNavigator() && /Edge/i.test(navigator.userAgent); + } + + //IE11 also lies about its user agent, but has Trident appear somewhere in + //the user agent. Detecting the different between IE11 and Microsoft Edge isn't + //super-important because the UI is similar enough + export function isIE(): boolean { + return hasNavigator() && /Trident/i.test(navigator.userAgent); + } + + //Microsoft Edge and IE11 lie about being Chrome. Chromium-based Edge ("Edgeium") will be detected as Chrome, that is ok. If you're looking for Edgeium, use `isChromiumEdge()`. + export function isChrome(): boolean { + return !isEdge() && !isIE() && !!navigator && (/Chrome/i.test(navigator.userAgent) || /Chromium/i.test(navigator.userAgent)); + } + + //Chrome and Microsoft Edge lie about being Safari + export function isSafari(): boolean { + //Could also check isMac but I don't want to risk excluding iOS + //Checking for iPhone, iPod or iPad as well as Safari in order to detect home screen browsers on iOS + return !isChrome() && !isEdge() && !!navigator && /(Macintosh|Safari|iPod|iPhone|iPad)/i.test(navigator.userAgent); + } + + //Safari and WebKit lie about being Firefox + export function isFirefox(): boolean { + return !isSafari() && !!navigator && (/Firefox/i.test(navigator.userAgent) || /Seamonkey/i.test(navigator.userAgent)); + } + +} \ No newline at end of file diff --git a/targetconfig.json b/targetconfig.json index f0edc7f7981..94d6f38a3a8 100644 --- a/targetconfig.json +++ b/targetconfig.json @@ -1,269 +1,519 @@ { "packages": { - "approvedRepos": [ - "microsoft/pxt-neopixel", - "microsoft/pxt-microturtle", - "microsoft/pxt-sonar", - "microsoft/pxt-hacking-stem", - "microsoft/pxt-bluetooth-temperature-sensor", - "microsoft/pxt-bluetooth-midi", - "microsoft/pxt-max6675", - "microsoft/pxt-midi", - "microsoft/pxt-radio-blockchain", - "microsoft/pxt-bluetooth-max6675", - "microsoft/pxt-ws2812b", - "microsoft/pxt-apa102", - "microsoft/pxt-radio-firefly", - "KitronikLtd/pxt-kitronik-servo-lite", - "KitronikLtd/pxt-kitronik-motor-driver", - "KitronikLtd/pxt-kitronik-I2C-16-servo", - "KitronikLtd/pxt-kitronik-stopbit", - "KitronikLtd/pxt-kitronik-lampbit", - "KitronikLtd/pxt-kitronik-klimate", - "KitronikLtd/pxt-kitronik-zip-64", - "KitronikLtd/pxt-kitronik-rtc", - "KitronikLtd/pxt-kitronik-game-controller", - "KitronikLtd/pxt-kitronik-robotics-board", - "KitronikLtd/pxt-kitronik-klef-piano", - "adafruit/pxt-crickit", - "adafruit/pxt-seesaw", - "Seeed-Studio/pxt-grove", - "Seeed-Studio/pxt-grove-zero-for-microbit", - "Tinkertanker/pxt-ir-receiver", - "Tinkertanker/pxt-iot-environment-kit", - "Tinkertanker/pxt-motorbit", - "Tinkertanker/pxt-realtimeclock-ds1307", - "Tinkertanker/pxt-tinkercademy-tinker-kit", - "Tinkertanker/pxt-rotary-encoder-ky040", - "Tinkertanker/pxt-tinkercademy-microbot", - "Tinkertanker/pxt-oled-ssd1306", - "Tinkertanker/pxt-range-vl53l0x", - "Tinkertanker/pxt-continuous-servo", - "Tinkertanker/pxt-joystickbit", - "Tinkertanker/pxt-robit", - "Tinkertanker/pxt-smarthome", - "Tinkertanker/microDriver_SHT2x", - "Tinkertanker/pxt-ringbitcar", - "Tinkertanker/uDriver_PCA9585", - "Tinkertanker/pxt-alphanumeric-ht16k33", - "Tinkertanker/pxt-stepper-motor", - "CoderDojoOlney/pxt-olney", - "PaulDFoster/pxt-microbit-GY521", - "chevyng/pxt-ucl-junkrobot", - "sparkfun/pxt-gamer-bit", - "sparkfun/pxt-moto-bit", - "sparkfun/pxt-weather-bit", - "sparkfun/pxt-gator-environment", - "minodekit/pxt-minode", - "LaboratoryForPlayfulComputation/pxt-BlockyTalkyBLE", - "mbitfun/pxt-katakana", - "jdarling/pxt-pca9685", - "MUSELAB/pxt-wifi-shield", - "kittenbot/pxt-robotbit", - "pizayanz/pxt-linebeacon", - "sunfounder/pxt-sloth", - "4tronix/BitBot", - "pimoroni/pxt-scrollbit", - "emwta/pxt-iBit", - "vengit/pxt-sbrick", - "pimoroni/pxt-envirobit", - "Annikken/pxt-Andee", - "1010Technologies/pxt-makerbit", - "1010Technologies/pxt-makerbit-motor", - "1010Technologies/pxt-makerbit-mp3", - "1010Technologies/pxt-makerbit-ultrasonic", - "1010Technologies/pxt-makerbit-lcd1602", - "1010Technologies/pxt-makerbit-ir-receiver", - "1010Technologies/pxt-makerbit-touch", - "1010Technologies/pxt-makerbit-pins", - "pimoroni/pxt-automationbit", - "k8robotics/pxt-k8", - "dexterind/pxt-giggle", - "dexterind/pxt-gigglebot", - "Imagimaker/pxt-imagimaker", - "sparkfun/pxt-gator-light", - "sparkfun/pxt-gator-temp", - "4tronix/Robobit", - "alsrobot-microbit-makecode-packages/ALSRobotJoyBit", - "alsrobot-microbit-makecode-packages/ALSRobotKeyboard", - "alsrobot-microbit-makecode-packages/ALSRobotElectromagnet", - "alsrobot-microbit-makecode-packages/CooCoo", - "alsrobot-microbit-makecode-packages/CruiseBit", - "makecode-extensions/i2cLCD1602", - "makecode-extensions/OLED12864_I2C", - "makecode-extensions/DS1307", - "makecode-extensions/ScrollText", - "makecode-extensions/WhaleySansFont", - "makecode-extensions/BMP280", - "makecode-extensions/TM1637", - "makecode-extensions/BMP180", - "makecode-extensions/BH1750", - "makecode-extensions/APDS9930", - "makecode-extensions/AT24XX", - "makecode-extensions/BME280", - "makecode-extensions/TM1650", - "makecode-extensions/NTC", - "makecode-extensions/DS1302", - "BirdBrainTechnologies/pxt-hummingbird-bit", - "PiSupply/pxt-iot-lora-node", - "PiSupply/pxt-tinker-kit", - "PiSupply/pxt-bitbuggy", - "PiSupply/pxt-oled-ssd1306", - "pimoroni/pxt-touchbit", - "4tronix/cubebit", - "4tronix/BitCommander", - "alankrantas/pxt-MAX7219_8x8", - "ReRoKit/pxt-reromicro", - "51bit/ColorBit", - "51bit/SFC", - "51bit/SmartTools", - "alankrantas/pxt-MAX7219_8x8", - "KitronikLtd/pxt-kitronik-zip-tile", - "lwchkg/pxt-proportional-font", - "jcubuntu/pxt-iKB1", - "KitronikLtd/pxt-kitronik-accessbit", - "kaku111/pxt-tobbieII", - "alankrantas/pxt-DHT11_DHT22", - "Freenove/Makecode-Extension-Rover", - "letstalkscience/pxt-cozir", - "e-radionicacom/pxt-wifi", - "monkmakes/pxt-sensor", - "beyond-coding-tw/pxt-nexusbot", - "elecfreaks/pxt-cutebot", - "KitronikLtd/pxt-kitronik-fischertechnik", - "keigan-motor/pxt-KeiganMotor", - "KitronikLtd/pxt-kitronik-klip-motor", - "alankrantas/pxt-ESP8266_ThingSpeak", - "KitronikLtd/pxt-kitronik-viewtext32", - "plenprojectcompany/pxt-PLENbit", - "4tronix/MiniBit", - "elecfreaks/pxt-wukong", - "sparkfun/pxt-gator-particle", - "sparkfun/pxt-gator-soil", - "sparkfun/pxt-gator-microphone", - "rebeccaclavier/pxt-bmp280", - "xinabox/pxt-SW01", - "xinabox/pxt-OD01", - "51bit/dfplayermini", - "makecode-extensions/STTS751", - "makecode-extensions/LSM6DSO", - "makecode-extensions/LPS22", - "makecode-extensions/LIS2DW12", - "makecode-extensions/LIS2MDL", - "makecode-extensions/HTS221", - "assirati/pxt-inventura", - "Veilkrand/pxt-RobotCar", - "4tronix/DriveBit", - "Freenove/Makecode-Extension-Starter-Kit", - "sphero-inc/sphero-sdk-microbit-makecode", - "BrightWearables/pxt-microbit-brightboard", - "EBOTICS/pxt-eboticsMIBO", - "KitronikLtd/pxt-kitronik-halohd", - "dugbraden/pxt-climate-action-kit", - "alsrobot-microbit-makecode-packages/MiniCruise", - "4tronix/ServoBit", - "DFRobot/pxt-maqueen", - "DFRobot/pxt-DFRobot-microIoT", - "mu-opensource/pxt-muvision", - "KitronikLtd/pxt-kitronik-clip-detector", - "DFRobot/pxt-DFRobot-NaturalScience", - "strawbees/pxt-robotic-inventions", - "daferdur/pxt-myHX711", - "CytronTechnologies/pxt-edubit", - "MakeAndLearn/pxt-microshield", - "4tronix/Orbit", - "elecfreaks/pxt-TPBot", - "longan-link/pxt-longanbit", - "CODOmicrobit/pxt-CODO", - "KitronikLtd/pxt-kitronik-move-motor", - "elecfreaks/pxt-PlanetX", - "elecfreaks/pxt-nezha", - "philipphenkel/pxt-powerfunctions", - "1010Technologies/pxt-makerbit-ir-transmitter", - "DFRobot/pxt-DFRobot_HuskyLens", - "bsiever/microbit-pxt-timeanddate", - "DFRobot/pxt-DFRobot-Maqueenplus", - "joy-it/Joy-Car", - "AlexandreFrolov/DS3231", - "elecfreaks/pxt-magicwand", - "YFROBOT-TM/pxt-yfrobot-valon", - "KitronikLtd/pxt-kitronik-smart-greenhouse", - "hardwario/pxt-microbit-hardwario", - "Schoumi/ssd1306-with-reset", - "keble6/pxt-DS3231", - "microsoft/ExpressivePixelsMakeCode", - "MUSELAB/pxt-muselab-oled-v2", - "microsoft/pxt-jacdac", - "CoolGuy-official/pxt-coolguy", - "Bouw-je-BEP/Bouw-je-BEP", - "BirdBrainTechnologies/pxt-finch" - ], - "preferredRepos": [ - "Microsoft/pxt-neopixel", - "Microsoft/pxt-microturtle", - "Tinkertanker/pxt-tinkercademy-tinker-kit", - "kittenbot/pxt-robotbit", - "Microsoft/pxt-sonar", - "4tronix/BitBot", - "kitronikltd/pxt-kitronik-servo-lite", - "kitronikltd/pxt-kitronik-motor-driver", - "Tinkertanker/pxt-ringbitcar" - ], - "upgrades": { - "tinkertanker/pxt-iot-environment-kit": "min:v4.2.1", - "microsoft/pxt-bluetooth-midi": "dv:mbcodal", - "laboratoryforplayfulcomputation/pxt-blockytalkyble": "dv:mbcodal", - "microsoft/pxt-bluetooth-temperature-sensor": "dv:mbcodal", - "minodekit/pxt-minode": "dv:mbcodal", - "sparkfun/pxt-gamer-bit": "dv:mbcodal", - "microsoft/pxt-bluetooth-max6675": "dv:mbcodal", - "pimoroni/pxt-scrollbit": "min:v0.0.7", - "pauldfoster/pxt-microbit-gy521": "dv:mbcodal", - "pizayanz/pxt-linebeacon": "min:v0.0.14", - "sparkfun/pxt-gator-environment": "dv:mbcodal", - "muselab/pxt-wifi-shield": "min:v1.8.82", - "tinkertanker/pxt-alphanumeric-ht16k33": "dv:mbcodal", - "tinkertanker/microdriver_sht2x": "dv:mbcodal", - "alsrobot-microbit-makecode-packages/cruisebit": "dv:mbcodal", - "tinkertanker/udriver_pca9585": "dv:mbcodal", - "dfrobot/pxt-dfrobot-naturalscience": "dv:mbcodal", - "kitronikltd/pxt-kitronik-zip-tile": "min:v0.1.0", - "4tronix/bitcommander": "min:1.1.1", - "tinkertanker/pxt-rotary-encoder-ky040": "dv:mbcodal", - "KitronikLtd/pxt-kitronik-zip-64": "min:v0.1.0", - "KitronikLtd/pxt-kitronik-game-controller": "min:v0.0.2", - "Tinkertanker/pxt-tinkercademy-microbot": "dv:mbcodal", - "Tinkertanker/pxt-range-vl53l0x": "dv:mbcodal", - "Imagimaker/pxt-imagimaker": "dv:mbcodal", - "PiSupply/pxt-oled-ssd1306": "dv:mbcodal", - "sparkfun/pxt-gator-particle": "dv:mbcodal", - "sparkfun/pxt-gator-microphone": "dv:mbcodal", - "rebeccaclavier/pxt-bmp280": "dv:mbcodal", - "mu-opensource/pxt-muvision": "dv:mbcodal", - "elecfreaks/pxt-PlanetX": "min:v0.13.1", - "bsiever/microbit-pxt-timeanddate": "min:v2.0.11" + "approvedRepoLib": { + "microsoft/pxt-neopixel": { + "tags": [ "Lights and Display" ], + "preferred": true + }, + "microbit-apps/display-shield": { + "tags": [ "Lights and Display" ], + "simx": { + "sha": "0825d56f78528c57af100a96abdfe2162f59945a", + "devUrl": "http://microbit-apps.github.io/display-shield/" + } + }, + "microsoft/pxt-microturtle": { + "tags": [ "Software" ], + "preferred": true + }, + "microsoft/pxt-sonar": { + "tags": [ "Science" ], + "preferred": true + }, + "microsoft/pxt-bluetooth-temperature-sensor": { + "tags": [ "Science" ], + "upgrades": [ "dv:mbcodal" ] + }, + "microsoft/pxt-bluetooth-midi": { + "upgrades": [ "dv:mbcodal" ] + }, + "microsoft/pxt-max6675": {}, + "microsoft/pxt-midi": {}, + "microsoft/pxt-radio-blockchain": {}, + "microsoft/pxt-bluetooth-max6675": { + "tags": [ "Science" ], + "upgrades": [ "dv:mbcodal" ] + }, + "microsoft/pxt-ws2812b": { "tags": [ "Lights and Display" ] }, + "microsoft/pxt-apa102": {}, + "microsoft/pxt-radio-firefly": {}, + "microsoft/pxt-ml": {}, + "kitronikltd/pxt-kitronik-servo-lite": { + "tags": [ "Robotics" ], + "preferred": true + }, + "kitronikltd/pxt-kitronik-motor-driver": { + "tags": [ "Robotics" ], + "preferred": true + }, + "kitronikltd/pxt-kitronik-i2c-16-servo": { "tags": [ "Robotics" ] }, + "kitronikltd/pxt-kitronik-stopbit": { "tags": [ "Robotics" ] }, + "kitronikltd/pxt-kitronik-lampbit": { "tags": [ "Lights and Display" ] }, + "kitronikltd/pxt-kitronik-klimate": { "tags": [ "Science" ] }, + "kitronikltd/pxt-kitronik-zip-64": { + "tags": [ "Gaming" ], + "upgrades": [ "min:v0.1.0" ] + }, + "kitronikltd/pxt-kitronik-rtc": {}, + "kitronikltd/pxt-kitronik-game-controller": { + "tags": [ "Gaming" ], + "upgrades": [ "min:v0.0.2" ] + }, + "kitronikltd/pxt-kitronik-robotics-board": { "tags": [ "Robotics" ] }, + "kitronikltd/pxt-kitronik-klef-piano": {}, + "adafruit/pxt-crickit": { "tags": [ "Science" ] }, + "adafruit/pxt-seesaw": { "tags": [ "Science" ] }, + "seeed-studio/pxt-grove": { + "tags": [ "Science" ], + "preferred": true + }, + "seeed-studio/pxt-grove-zero-for-microbit": {}, + "tinkertanker/pxt-ir-receiver": {}, + "tinkertanker/pxt-iot-environment-kit": { + "preferred": true, + "upgrades": [ "min:v4.2.1" ] + }, + "tinkertanker/pxt-motorbit": {}, + "tinkertanker/pxt-realtimeclock-ds1307": {}, + "tinkertanker/pxt-tinkercademy-tinker-kit": { "preferred": true }, + "tinkertanker/pxt-rotary-encoder-ky040": { + "tags": [ "Science" ], + "upgrades": [ "min:v1.2.1" ] + }, + "tinkertanker/pxt-tinkercademy-microbot": { "upgrades": [ "dv:mbcodal" ] }, + "tinkertanker/pxt-oled-ssd1306": { "tags": [ "Lights and Display" ] }, + "tinkertanker/pxt-range-vl53l0x": { "upgrades": [ "min:v1.0.1" ] }, + "tinkertanker/pxt-continuous-servo": {}, + "tinkertanker/pxt-joystickbit": {}, + "tinkertanker/pxt-robit": {}, + "tinkertanker/pxt-smarthome": { "preferred": true }, + "tinkertanker/microdriver_sht2x": { "upgrades": [ "min:v1.0.0" ] }, + "tinkertanker/pxt-ringbitcar": { "preferred": true }, + "tinkertanker/udriver_pca9585": { "upgrades": [ "dv:mbcodal" ] }, + "tinkertanker/pxt-alphanumeric-ht16k33": { "upgrades": [ "min:v1.1.0" ] }, + "tinkertanker/pxt-stepper-motor": { "tags": [ "Robotics" ] }, + "coderdojoolney/pxt-olney": {}, + "pauldfoster/pxt-microbit-gy521": { + "tags": [ "Science" ], + "upgrades": [ "dv:mbcodal" ] + }, + "chevyng/pxt-ucl-junkrobot": { "tags": [ "Robotics" ] }, + "sparkfun/pxt-gamer-bit": { + "tags": [ "Gaming" ] + }, + "sparkfun/pxt-moto-bit": { "tags": [ "Robotics" ] }, + "sparkfun/pxt-weather-bit": { "tags": [ "Science" ] }, + "sparkfun/pxt-gator-environment": { + "tags": [ "Science" ], + "upgrades": [ "min:v1.1.2" ] + }, + "minodekit/pxt-minode": { + "tags": [ "Science" ], + "upgrades": [ "dv:mbcodal" ] + }, + "laboratoryforplayfulcomputation/pxt-blockytalkyble": { + "tags": [ "Software" ], + "upgrades": [ "dv:mbcodal" ] + }, + "mbitfun/pxt-katakana": { "tags": [ "Software" ] }, + "jdarling/pxt-pca9685": { "tags": [ "Robotics" ] }, + "muselab/pxt-wifi-shield": { + "tags": [ "Networking" ], + "upgrades": [ "min:v1.8.82" ] + }, + "kittenbot/pxt-robotbit": { + "tags": [ "Robotics" ], + "preferred": true + }, + "pizayanz/pxt-linebeacon": { + "tags": [ "Software" ], + "upgrades": [ "min:v0.0.14" ] + }, + "sunfounder/pxt-sloth": { "tags": [ "Robotics" ] }, + "4tronix/bitbot": { + "tags": [ "Robotics" ], + "preferred": true + }, + "pimoroni/pxt-scrollbit": { + "tags": [ "Lights and Display" ], + "upgrades": [ "min:v0.0.7" ] + }, + "emwta/pxt-ibit": { "tags": [ "Robotics" ] }, + "vengit/pxt-sbrick": {}, + "pimoroni/pxt-envirobit": { "tags": [ "Science" ] }, + "annikken/pxt-andee": {}, + "1010technologies/pxt-makerbit": { "tags": [ "Science" ] }, + "1010technologies/pxt-makerbit-motor": { "tags": [ "Robotics" ] }, + "1010technologies/pxt-makerbit-mp3": {}, + "1010technologies/pxt-makerbit-ultrasonic": { "tags": [ "Science" ] }, + "1010technologies/pxt-makerbit-lcd1602": {}, + "1010technologies/pxt-makerbit-ir-receiver": { "tags": [ "Science" ] }, + "1010technologies/pxt-makerbit-touch": { "tags": [ "Science" ] }, + "1010technologies/pxt-makerbit-pins": { "tags": [ "Science" ] }, + "pimoroni/pxt-automationbit": { "tags": [ "Science" ] }, + "k8robotics/pxt-k8": { "tags": [ "Robotics" ] }, + "dexterind/pxt-giggle": { "tags": [ "Robotics" ] }, + "dexterind/pxt-gigglebot": {}, + "imagimaker/pxt-imagimaker": { + "tags": [ "Science" ], + "upgrades": [ "dv:mbcodal" ] + }, + "sparkfun/pxt-gator-light": { "tags": [ "Science" ] }, + "sparkfun/pxt-gator-temp": { "tags": [ "Science" ] }, + "4tronix/robobit": { "tags": [ "Robotics" ] }, + "alsrobot-microbit-makecode-packages/alsrobotjoybit": { "tags": [ "Gaming" ] }, + "alsrobot-microbit-makecode-packages/alsrobotkeyboard": {}, + "alsrobot-microbit-makecode-packages/alsrobotelectromagnet": { "tags": [ "Science" ] }, + "alsrobot-microbit-makecode-packages/coocoo": { "tags": [ "Robotics" ] }, + "alsrobot-microbit-makecode-packages/cruisebit": { + "tags": [ "Robotics" ], + "upgrades": [ "dv:mbcodal" ] + }, + "makecode-extensions/i2clcd1602": {}, + "makecode-extensions/oled12864_i2c": {}, + "makecode-extensions/ds1307": {}, + "makecode-extensions/scrolltext": {}, + "makecode-extensions/whaleysansfont": {}, + "makecode-extensions/bmp280": {}, + "makecode-extensions/tm1637": {}, + "makecode-extensions/bmp180": {}, + "makecode-extensions/bh1750": {}, + "makecode-extensions/apds9930": {}, + "makecode-extensions/at24xx": {}, + "makecode-extensions/bme280": {}, + "makecode-extensions/tm1650": {}, + "makecode-extensions/ntc": {}, + "makecode-extensions/ds1302": {}, + "birdbraintechnologies/pxt-hummingbird-bit": { "tags": [ "Robotics" ] }, + "pisupply/pxt-iot-lora-node": { "tags": [ "Networking" ] }, + "pisupply/pxt-tinker-kit": { "tags": [ "Science" ] }, + "pisupply/pxt-bitbuggy": { "tags": [ "Robotics" ] }, + "pisupply/pxt-oled-ssd1306": { "upgrades": [ "dv:mbcodal" ]}, + "pimoroni/pxt-touchbit": { "tags": [ "Gaming" ] }, + "4tronix/cubebit": { "tags": [ "Lights and Display" ] }, + "4tronix/bitcommander": { + "tags": [ "Gaming" ], + "upgrades": [ "min:v1.1.1" ] + }, + "alankrantas/pxt-max7219_8x8": { "tags": [ "Lights and Display" ] }, + "rerokit/pxt-reromicro": { "tags": [ "Robotics" ] }, + "51bit/colorbit": { "tags": [ "Lights and Display" ] }, + "51bit/sfc": { "tags": [ "Gaming" ] }, + "51bit/smarttools": { "tags": [ "Science" ] }, + "kitronikltd/pxt-kitronik-zip-tile": { + "tags": [ "Lights and Display" ], + "upgrades": [ "min:v0.1.0" ] + }, + "lwchkg/pxt-proportional-font": {}, + "jcubuntu/pxt-ikb1": { "tags": [ "Robotics" ] }, + "kitronikltd/pxt-kitronik-accessbit": { "tags": [ "Robotics" ] }, + "kaku111/pxt-tobbieii": { "tags": [ "Robotics" ] }, + "alankrantas/pxt-dht11_dht22": { "tags": [ "Science" ] }, + "freenove/makecode-extension-rover": { "tags": [ "Robotics" ] }, + "letstalkscience/pxt-cozir": { "tags": [ "Science" ] }, + "solderedelectronics/pxt-wifi": { "tags": [ "Networking" ] }, + "monkmakes/pxt-sensor": { "tags": [ "Science" ] }, + "beyond-coding-tw/pxt-nexusbot": { "tags": [ "Robotics" ] }, + "elecfreaks/pxt-cutebot": { + "tags": [ "Robotics" ], + "preferred": true + }, + "kitronikltd/pxt-kitronik-fischertechnik": { "tags": [ "Robotics" ] }, + "keigan-motor/pxt-keiganmotor": { "tags": [ "Robotics" ] }, + "kitronikltd/pxt-kitronik-klip-motor": { "tags": [ "Robotics" ] }, + "alankrantas/pxt-esp8266_thingspeak": { "tags": [ "Networking" ] }, + "kitronikltd/pxt-kitronik-viewtext32": { "tags": [ "Lights and Display" ] }, + "plenprojectcompany/pxt-plenbit": { "tags": [ "Robotics" ] }, + "4tronix/minibit": { "tags": [ "Robotics" ] }, + "elecfreaks/pxt-wukong": { "tags": [ "Science" ] }, + "sparkfun/pxt-gator-particle": { + "tags": [ "Science" ], + "upgrades": [ "min:v1.1.6" ] + }, + "sparkfun/pxt-gator-soil": { "tags": [ "Science" ] }, + "sparkfun/pxt-gator-microphone": { + "tags": [ "Science" ], + "upgrades": [ "min:v1.0.21" ] + }, + "rebeccaclavier/pxt-bmp280": { + "tags": [ "Science" ], + "upgrades": [ "dv:mbcodal" ] + }, + "xinabox/pxt-sw01": { "tags": [ "Science" ] }, + "xinabox/pxt-od01": { "tags": [ "Lights and Display" ] }, + "51bit/dfplayermini": {}, + "makecode-extensions/stts751": { "tags": [ "Science" ] }, + "makecode-extensions/lsm6dso": { "tags": [ "Science" ] }, + "makecode-extensions/lps22": { "tags": [ "Science" ] }, + "makecode-extensions/lis2dw12": { "tags": [ "Science" ] }, + "makecode-extensions/lis2mdl": { "tags": [ "Science" ] }, + "makecode-extensions/hts221": { "tags": [ "Science" ] }, + "assirati/pxt-inventura": {}, + "veilkrand/pxt-robotcar": { "tags": [ "Robotics" ] }, + "4tronix/drivebit": { "tags": [ "Robotics" ] }, + "freenove/makecode-extension-starter-kit": { "tags": [ "Science" ] }, + "sphero-inc/sphero-sdk-microbit-makecode": { "tags": [ "Robotics" ] }, + "brightwearables/pxt-microbit-brightboard": {}, + "ebotics/pxt-eboticsmibo": { "tags": [ "Robotics" ] }, + "kitronikltd/pxt-kitronik-halohd": { "tags": [ "Lights and Display" ] }, + "climate-action-kits/pxt-climate-action-kit-land": { "tags": [ "Science" ] }, + "alsrobot-microbit-makecode-packages/minicruise": { "tags": [ "Robotics" ] }, + "4tronix/servobit": { "tags": [ "Robotics" ] }, + "dfrobot/pxt-maqueen": { + "tags": [ "Robotics" ], + "preferred": true + }, + "dfrobot/pxt-dfrobot-microiot": { "tags": [ "Networking" ] }, + "mu-opensource/pxt-muvision": { + "tags": [ "Science" ], + "upgrades": [ "min:v1.2.28" ] + }, + "kitronikltd/pxt-kitronik-clip-detector": { "tags": [ "Science" ] }, + "dfrobot/pxt-dfrobot-naturalscience": { + "tags": [ "Science" ], + "upgrades": [ "dv:mbcodal" ] + }, + "strawbees/pxt-robotic-inventions": { "tags": [ "Robotics" ] }, + "daferdur/pxt-myhx711": { "tags": [ "Science" ] }, + "cytrontechnologies/pxt-edubit": { "tags": [ "Science" ] }, + "makeandlearn/pxt-microshield": { "tags": [ "Science" ] }, + "4tronix/orbit": { "tags": [ "Robotics" ] }, + "elecfreaks/pxt-tpbot": { "tags": [ "Robotics" ] }, + "longan-link/pxt-longanbit": { "tags": [ "Science" ] }, + "codomicrobit/pxt-codo": { "tags": [ "Robotics" ] }, + "kitronikltd/pxt-kitronik-move-motor": { "tags": [ "Robotics" ] }, + "elecfreaks/pxt-planetx": { + "tags": [ "Science" ], + "upgrades": [ "min:v0.13.1" ] + }, + "elecfreaks/pxt-nezha": { "tags": [ "Robotics" ] }, + "elecfreaks/pxt-nezha2": { "tags": [ "Robotics" ] }, + "philipphenkel/pxt-powerfunctions": {}, + "1010technologies/pxt-makerbit-ir-transmitter": {}, + "dfrobot/pxt-dfrobot_huskylens": { "tags": [ "Science" ] }, + "bsiever/microbit-pxt-timeanddate": { + "tags": [ "Software" ], + "upgrades": [ "min:v2.0.11" ] + }, + "dfrobot/pxt-dfrobot-maqueenplus": { + "tags": [ "Robotics" ], + "preferred": true + }, + "dfrobot/pxt-dfrobot_maqueenplus_v20": { + "tags": [ "Robotics" ] + }, + "joy-it/joy-car": { "tags": [ "Robotics" ] }, + "alexandrefrolov/ds3231": {}, + "elecfreaks/pxt-magicwand": { "tags": [ "Gaming" ] }, + "yfrobot-tm/pxt-yfrobot-valon": { "tags": [ "Robotics" ] }, + "kitronikltd/pxt-kitronik-smart-greenhouse": { "tags": [ "Science" ] }, + "hardwario/pxt-microbit-hardwario": { "tags": [ "Networking" ] }, + "schoumi/ssd1306-with-reset": {}, + "keble6/pxt-ds3231": { "tags": [ "Science" ] }, + "microsoft/expressivepixelsmakecode": {}, + "pimoroni/pxt-inkybit": { "tags": [ "Lights and Display" ] }, + "muselab/pxt-muselab-oled-v2": { "tags": [ "Lights and Display" ] }, + "microsoft/pxt-jacdac": { + "tags": [ "Science" ], + "upgrades": [ "move:jacdac/pxt-jacdac" ], + "hidden": true + }, + "coolguy-official/pxt-coolguy": {}, + "bouw-je-bep/bouw-je-bep": { "tags": [ "Robotics" ] }, + "birdbraintechnologies/pxt-finch": { "tags": [ "Robotics" ] }, + "ibuilds/pxt-ptkidsbit": { "tags": [ "Science" ] }, + "wappsto/pxt-wappsto": { "tags": [ "Networking" ] }, + "ks-bulme/pxt-mikrobot": { "tags": [ "Robotics" ] }, + "cytrontechnologies/pxt-rekabit": { "tags": [ "Science" ] }, + "bsiever/microbit-dstemp": { "tags": [ "Science" ] }, + "bsiever/microbit-dstemp-2wire": { "tags": [ "Science" ] }, + "microsoft/pxt-data-streamer": {}, + "ibuilds/pxt-ptkidsbit-robot": { "tags": [ "Robotics" ] }, + "4tronix/eggbit": {}, + "joy-it/sen-mpu6050": { "tags": [ "Science" ] }, + "gomakekit/hoverbit_v2": { "tags": [ "Robotics" ] }, + "matrix-robotics/pxt-matrixmicro": {}, + "joy-it/pxt-rb-tft1.8": { "tags": [ "Lights and Display" ] }, + "cytrontechnologies/pxt-esp8266": { "tags": [ "Networking" ] }, + "elecfreaks/pxt-dronebit": {}, + "elecfreaks/pxt-planetx-ai": { "tags": [ "Science" ] }, + "joy-it/pxt-sen-color": { "tags": [ "Science" ] }, + "stemhub/pxt-stemhubbit": { "tags": [ "Robotics" ] }, + "kitronikltd/pxt-kitronik-lab-bit": { "tags": [ "Science" ] }, + "kitronikltd/pxt-kitronik-128x64display": { "tags": [ "Lights and Display" ] }, + "monkmakes/monkmakes-7-segment": { "tags": [ "Lights and Display" ] }, + "stemhub/pxt-stemhubcity": { "tags": [ "Science" ] }, + "kittenbot/pxt-powerbrick": { "tags": [ "Science" ] }, + "microbit-foundation/pxt-microbit-v2-power": { "tags": [ "Software" ] }, + "microbit-foundation/pxt-sound-level-db": { + "tags": [ "Software" ], + "upgrades": [ "min:v0.1.13" ] + }, + "kittenbot/pxt-joyfrog": { "tags": [ "Gaming" ] }, + "kittenbot/pxt-sugar": { "tags": [ "Science" ] }, + "kittenbot/pxt-koi": { "tags": [ "Science" ] }, + "kittenbot/pxt-koi2": { "tags": [ "Science" ] }, + "kidspark/pxt-sparkbit": { "tags": [ "Robotics" ] }, + "bpi-steam/pxt-triodecar": { "tags": [ "Robotics" ] }, + "kitronikltd/pxt-kitronik-air-quality": { "tags": [ "Science" ] }, + "kitronikltd/pxt-kitronik-air-quality-v2-only": {}, + "artec-kk/pxt-artecrobo-kit": { "tags": [ "Robotics" ] }, + "teacherpinky/wait-until-blocks": { "tags": [ "Software" ] }, + "kittenbot/pxt-kittenwifi": { "tags": [ "Networking" ] }, + "sgbotic/pxt-sgbotic-ultimate-sr04-rgb": { "tags": [ "Science" ] }, + "cytrontechnologies/pxt-zoombit": { "tags": [ "Robotics" ] }, + "kodely-io/dot": { "tags": [ "Software" ] }, + "climate-action-kits/pxt-climate-action-kit-energy": {}, + "kitronikltd/pxt-kitronik-simple-servo": { "tags": [ "Robotics" ] }, + "hackidsedu/pxt-hackbit": { "tags": [ "Science" ] }, + "aorczyk/lego-pf-transmitter": {}, + "kittenbot/pxt-minilfr": { "tags": [ "Robotics" ] }, + "ibuilds/pxt-ptkidsbit-iot": { "tags": [ "Science" ] }, + "kelieleung/pxt-iclassiot": { "tags": [ "Networking" ] }, + "aorczyk/lego-pf-receiver": {}, + "smarthon/pxt-smartcity": { "tags": [ "Science" ] }, + "aorczyk/soroban": { "tags": [ "Software" ] }, + "aorczyk/pf-recorder": {}, + "4tronix/theta": { "tags": [ "Robotics" ] }, + "bsiever/microbit-pxt-blehid": {}, + "dfrobot/pxt-dfrobot_environment_science": {}, + "ekkai/aicococam": {}, + "elecfreaks/pxt-xgo": {}, + "joy-it/pxt-rfid-mfrc522": { "tags": [ "Science" ] }, + "dfrobot/pxt-dfrobot_iot_cloud_kit": { "tags": [ "Networking" ] }, + "plenprojectcompany/pxt-plenbit_full": { "tags": [ "Robotics" ] }, + "bsiever/microbit-pxt-clicks": { "tags": [ "Software" ] }, + "bsiever/pxt-morse": { "tags": [ "Software" ] }, + "joy-it/pxt-ads1115": { "tags": [ "Science" ] }, + "bsiever/microbit-pxt-rotate": { "tags": [ "Software" ] }, + "sparkfun/pxt-gator-uv": { "tags": [ "Science" ] }, + "dfrobot/pxt-dfrobot_bosonkit": { "tags": [ "Science" ] }, + "resolute-support/pxt-apprentice_car": { "tags": [ "Robotics" ] }, + "makeandlearn/pxt-didacbot": { "tags": [ "Robotics" ] }, + "cytrontechnologies/pxt-rekabit-rbt-project-kit": { "tags": [ "Science" ] }, + "cytrontechnologies/pxt-motionbit": { "tags": [ "Robotics" ] }, + "joy-it/pxt-rb-joypi-advanced": { "tags": [ "Science" ] }, + "bsiever/pxt-sen55": { "tags": [ "Science" ] }, + "climate-action-kits/pxt-fwd-edu": {}, + "elecfreaks/pxt-cutebot-pro": { "tags": [ "Robotics" ], "preferred": true }, + "microbit-foundation/makecode-tutorials": { "tutorial" : true }, + "grandpabond/pxt-meter": { "tags": [ "Software" ] }, + "microsoft/microbit-robot": { "tags": [ "Robotics" ] }, + "4tronix/mars-rover": { "tags": [ "Robotics" ] }, + "monkmakes/plant-monitor-makecode": { "tags": [ "Science" ] }, + "grandpabond/pxt-flexfx": { "tags": [ "Software" ] }, + "grandpabond/pxt-faces": { "tags": [ "Software" ] }, + "joylabz/code-a-key-extension": {}, + "eb8ga/pxt-roversa-2": { "tags": [ "Robotics" ] }, + "roborisen/gcube": { "tags": [ "Robotics" ] }, + "kittenbot/pxt-tabbyrobot": { "tags": [ "Robotics" ] }, + "pythom1234/pxt-oled": { "tags": [ "Lights and Display" ] }, + "softsmyth/lectrify-b4k": { "tags": [ "Robotics" ] }, + "davidnsousa/sonification": { "tags": [ "Software" ] }, + "shahart/heb-microbit": { "tags": [ "Software" ] }, + "kitronikltd/pxt-kitronik-craft-and-code": { "tags": [ "Robotics" ] }, + "cytrontechnologies/pxt-sumobit": { "tags": [ "Robotics" ] }, + "hovavo/pxt-states": { "tags": [ "Software" ] }, + "parallaxinc/cyberbot_makecode": { "tags": [ "Robotics" ] }, + "siyeenove/pxt_mcar": { "tags": [ "Robotics" ] }, + "elecfreaks/xgo-rider": { "tags": [ "Robotics" ] }, + "dfrobot/pxt-dfrobot_creative-robotics-kit": { "tags": [ "Robotics" ] }, + "ines-hpmm/pxt-luma-matrix": { "tags": [ "Lights and Display" ] }, + "team-bp/pxt-bplab": { "tags": [ "Science" ] }, + "jimd80/pxt-coderdojo-controller": { "tags": [ "Gaming" ] }, + "smarthon/pxt-iot-bit": { "tags": [ "Networking" ] }, + "forward-education/pxt-smart-soldering": { "tags": [ "Science" ] }, + "forward-education/pxt-smart-solar": { "tags": [ "Science" ] }, + "forward-education/pxt-smart-hydroponics": { "tags": [ "Science" ] }, + "forward-education/pxt-all-fwd-blocks": { "tags": [ "Science" ] }, + "forward-education/pxt-climate-action": { "tags": [ "Science" ] }, + "elecfreaks/pxt-petal": { "tags": [ "Science" ] }, + "kitronikltd/pxt-kitronik-mai-z": { "tags": [ "Robotics" ] }, + "smarthon/pxt-smarthome": { "tags": [ "Networking" ] }, + "roborisen/braillebot": { "tags": [ "Robotics" ] }, + "backyardbrains/pxt-spikerbit": { "tags": [ "Science" ] }, + "siyeenove/pxt_mshield": { "tags": [ "Robotics" ] }, + "siyeenove/pxt_pybit": { "tags": [ "Robotics" ] }, + "nathanpervin/pxt-tm1638": { "tags": [ "Lights and Display" ] }, + "bestmodules-libraries/pxt-bmduino": { "tags": [ "Science" ] }, + "forward-education/pxt-coding-for-good": { "tags": [ "Science" ] }, + "dfrobot/pxt-dfrobot_huskylensv2": { "tags": [ "Science" ] }, + "steveturbek/pxt-rotary-encoder-ky-040-plus": { "tags": [ "Science" ] }, + "jim-no-surname-provided/pxt-tobbieii": { "tags": [ "Robotics" ] }, + "pyocodingcompany-crypto/pyobot-makecode": { "tags": [ "Robotics" ] }, + "kitronikltd/pxt-design-and-automate-accessory-kit": { "tags": [ "Robotics" ] }, + "aorczyk/my-controller": { "tags": [ "Software" ] }, + "forward-education/pxt-fwd-ubit": {}, + "forward-education/pxt-openscied": { "tags": [ "Science" ] }, + "forward-education/pxt-ceibal-ubit": {}, + "bsiever/pxt-sen66": { "tags": [ "Science" ] }, + "skinformatics/enorasiscore-makecode": { "tags": [ "Science" ] }, + "robotgyms/pxt-robotpu": { "tags": [ "Robotics" ] }, + "pasalt/pxt-neopixel-matrix-extension": { "tags": [ "Lights and Display" ] }, + "peanut-king-solution/pxt-pks-shield-v2": { "tags": [ "Robotics" ] }, + "peanut-king-solution/pxt-pks-controller": { "tags": [ "Robotics" ] }, + "forward-education/pxt-ai-vision": { "tags": [ "Science" ] }, + "forward-education/pxt-ai-voice": { "tags": [ "Science" ] }, + "elecfreaks/pxt-pu-robot": { "tags": [ "Robotics" ] }, + "microsoft/pxt-simx-sample": { + "simx": { + "sha": "7301f5900879b85127482d79bab48f03c25690a8", + "devUrl": "http://localhost:5173" + } + }, + "microbit-foundation/pxt-microbit-ml": { + "simx": { + "sha": "08b9e007964a25bb7dfe3f00d7fc2450102f1bfc", + "devUrl": "http://localhost:5173", + "aspectRatio": 3.45 + }, + "hidden": true + }, + "jacdac/pxt-jacdac": { + "simx": { + "sha": "1804c0b3d2976935c476f3b6d425b554a20fa814", + "devUrl": "https://jacdac.github.io/jacdac-docs/tools/makecode-sim/" + } + }, + "jacdac/pxt-jacdac-test": { + "hidden": true, + "simx": { + "sha": "8bab6a87265dc3e1e008a716a478306964b6d889", + "devUrl": "https://jacdac.github.io/pxt-jacdac-test/index.html" + } + } }, "approvedEditorExtensionUrls": [ - "https://microsoft.github.io/jacdac-ts/tools/makecode-editor-extension" + "https://microsoft.github.io/ml4f/" + ], + "extensionsToolboxDisallowDelete": [ ] }, "galleries": { "Tutorials": "tutorials", + "Tutorials for the new micro:bit (V2)": "tutorials-v2", + "Games": "projects/games", + "Make it: code it Examples": "microbit-org/make-it-code-it", + "Radio Games": "projects/radio-games", + "Data Logging Examples": "microbit-org/data-logging", "Live Coding": { "url": "live-coding", "shuffle": "daily", "youTube": true }, - "Games": "projects/games", - "Radio Games": "projects/radio-games", "Fashion": "projects/fashion", "Music": "projects/music", "Toys": "projects/toys", "Science": "projects/science", "Tools": "projects/tools", "Turtle": "projects/turtle", - "Blocks To JavaScript": "courses/blocks-to-javascript", + "Blocks to JavaScript": "courses/blocks-to-javascript", + "First Lessons with MakeCode and the micro:bit": "microbit-org/first-lessons", + "CreateAI": "microbit-org/createai", "Courses": "courses", + "Jacdac": "jacdac", + "MicroCode for the new micro:bit (V2)": "microcode", + "Introductory micro:bit Feature Videos": "microbit-org/feature-videos", "Behind the MakeCode Hardware": { "url": "behind-the-makecode-hardware", "youTube": true @@ -272,6 +522,7 @@ "url": "science-experiments", "youTube": true }, + "Educator Professional Development": "microbit-org/professional-development", "Coding for Teachers": { "url": "coding-for-teachers", "youTube": true @@ -282,7 +533,29 @@ "youTube": true } }, + "teachertool": { + "showSharePageEvalButton": true, + "defaultChecklistUrl": "teachertool/checklists/general-code-quality.json", + "carousels": [ + { + "title": "Checklists for Tutorials", + "cardsUrl": "teachertool/carousels/checklists-for-tutorials/cards.json" + }, + { + "title": "Checklists for V2 Tutorials", + "cardsUrl": "teachertool/carousels/checklists-for-tutorials-v2/cards.json" + }, + { + "title": "Checklists for Games", + "cardsUrl": "teachertool/carousels/checklists-for-games/cards.json" + }, + { + "title": "Checklists for Tools", + "cardsUrl": "teachertool/carousels/checklists-for-tools/cards.json" + } + ] + }, "electronManifest": { - "latest": "v3.0.65" + "latest": "v9.0.8" } } diff --git a/theme/blockly-toolbox.less b/theme/blockly-toolbox.less index cdeca055462..f48181d2bd0 100644 --- a/theme/blockly-toolbox.less +++ b/theme/blockly-toolbox.less @@ -4,7 +4,7 @@ *******************************/ div.blocklyTreeRow { - box-shadow: inset 0 -1px 0 0 #ecf0f1; + box-shadow: inset 0 -1px 0 0 var(--pxt-target-stencil3); margin-bottom: 0px !important; @@ -20,15 +20,15 @@ span.blocklyTreeLabel { font-weight: 200; } -.blocklyToolboxDiv, .monacoToolboxDiv { - background-color: white !important; - border-left: 1px solid #ecf0f1 !important; - box-shadow: 4px 0px 2px -4px rgba(0,0,0,0.12), 4px 0px 2px -4px rgba(0,0,0,0.24); +.blocklyToolbox, .monacoToolboxDiv { + background-color: var(--pxt-target-background3) !important; + color: var(--pxt-target-foreground3); + box-shadow: 4px 0px 2px -4px var(--pxt-neutral-alpha10), 4px 0px 2px -4px var(--pxt-neutral-alpha20); } /* Mobile */ @media only screen and (max-width: @largestMobileScreen) { - .blocklyToolboxDiv, .monacoToolboxDiv { + .blocklyToolbox, .monacoToolboxDiv { border-left: 0 !important; } div.blocklyTreeRoot { @@ -38,7 +38,7 @@ span.blocklyTreeLabel { /* Tablet */ @media only screen and (min-width: @tabletBreakpoint) and (max-width: @largestTabletScreen) { - .blocklyToolboxDiv, .monacoToolboxDiv { + .blocklyToolbox, .monacoToolboxDiv { border-left: 0 !important; } div.blocklyTreeRoot { diff --git a/theme/blockly.less b/theme/blockly.less index f8e9ba238d2..f062afca2a5 100644 --- a/theme/blockly.less +++ b/theme/blockly.less @@ -32,4 +32,12 @@ text.blocklyText { .blocklyLedOn { stroke: white; stroke-width: 1px; +} + +.blocklyWidgetDiv .blocklyGridPickerScroller.keyboardNavigable:has(:focus-visible) { + outline: none; + + .blocklyGridPickerMenu:focus .blocklyGridPickerRow .gridpicker-menuitem.gridpicker-option-focused { + outline: 4px solid white; + } } \ No newline at end of file diff --git a/theme/color-themes/microbit-dark.json b/theme/color-themes/microbit-dark.json new file mode 100644 index 00000000000..586859eba21 --- /dev/null +++ b/theme/color-themes/microbit-dark.json @@ -0,0 +1,122 @@ +{ + "id": "microbit-dark", + "name": "Dark", + "weight": 60, + "monacoBaseTheme": "vs-dark", + "overrideFiles": [ + "/overrides/microbit-dark-overrides.css" + ], + "colors": { + "pxt-header-background": "#181818", + "pxt-header-foreground": "#ffffff", + "pxt-header-background-hover": "#252525", + "pxt-header-foreground-hover": "#ffffff", + "pxt-header-stencil": "#323232", + + "pxt-primary-background": "#0078D4", + "pxt-primary-foreground": "#ffffff", + "pxt-primary-background-hover": "#026EC1", + "pxt-primary-foreground-hover": "#ffffff", + "pxt-primary-accent": "#005ba1", + + "pxt-secondary-background": "#63276d", + "pxt-secondary-foreground": "#f3f2f1", + "pxt-secondary-background-hover": "#742e80", + "pxt-secondary-foreground-hover": "#f3f2f1", + "pxt-secondary-accent": "#411a47", + + "pxt-tertiary-background": "#0078d4", + "pxt-tertiary-foreground": "#ffffff", + "pxt-tertiary-background-hover": "#026EC1", + "pxt-tertiary-foreground-hover": "#ffffff", + "pxt-tertiary-accent": "#0894ff", + + "pxt-target-background1": "#2d2d2d", + "pxt-target-foreground1": "#f3f2f1", + "pxt-target-background1-hover": "#202020", + "pxt-target-foreground1-hover": "#ffffff", + "pxt-target-stencil1": "#3b3a39", + + "pxt-target-background2": "#181818", + "pxt-target-foreground2": "#ffffff", + "pxt-target-background2-hover": "#252525", + "pxt-target-foreground2-hover": "#ffffff", + "pxt-target-stencil2": "#323232", + + "pxt-target-background3": "#1F1F1F", + "pxt-target-foreground3": "#ffffff", + "pxt-target-background3-hover": "#252525", + "pxt-target-foreground3-hover": "#ffffff", + "pxt-target-stencil3": "#323232", + + "pxt-neutral-background1": "#1F1F1F", + "pxt-neutral-foreground1": "#f3f2f1", + "pxt-neutral-background1-hover": "#2c2c2c", + "pxt-neutral-foreground1-hover": "#ffffff", + "pxt-neutral-stencil1": "#3b3a39", + + "pxt-neutral-background2": "#181818", + "pxt-neutral-foreground2": "#ffffff", + "pxt-neutral-background2-hover": "#252525", + "pxt-neutral-foreground2-hover": "#ffffff", + "pxt-neutral-stencil2": "#3b3a39", + + "pxt-neutral-background3": "#202020", + "pxt-neutral-foreground3": "#f3f2f1", + "pxt-neutral-background3-hover": "#131313", + "pxt-neutral-foreground3-hover": "#ffffff", + "pxt-neutral-stencil3": "#070707", + "pxt-neutral-background3-alpha90": "#202020e6", + + "pxt-neutral-base": "rgba(180, 180, 180, 1)", + "pxt-neutral-alpha0": "rgba(180, 180, 180, 0)", + "pxt-neutral-alpha10": "rgba(180, 180, 180, 0.1)", + "pxt-neutral-alpha20": "rgba(180, 180, 180, 0.2)", + "pxt-neutral-alpha50": "rgba(180, 180, 180, 0.5)", + "pxt-neutral-alpha80": "rgba(180, 180, 180, 0.8)", + + "pxt-link": "#479ef5", + "pxt-link-hover": "#62abf5", + "pxt-focus-border": "#5caae5", + + "pxt-colors-purple-background": "#63276d", + "pxt-colors-purple-foreground": "#f3f2f1", + "pxt-colors-purple-hover": "#742e80", + "pxt-colors-purple-alpha10": "#63276d19", + + "pxt-colors-orange-background": "#7a2101", + "pxt-colors-orange-foreground": "#ffffff", + "pxt-colors-orange-hover": "#932801", + "pxt-colors-orange-alpha10": "#7a210119", + + "pxt-colors-brown-background": "#50301a", + "pxt-colors-brown-foreground": "#ffffff", + "pxt-colors-brown-hover": "#633c20", + "pxt-colors-brown-alpha10": "#50301a19", + + "pxt-colors-blue-background": "#0078D4", + "pxt-colors-blue-foreground": "#ffffff", + "pxt-colors-blue-hover": "#026EC1", + "pxt-colors-blue-alpha10": "#0078D419", + + "pxt-colors-green-background": "#27ae60", + "pxt-colors-green-foreground": "#ffffff", + "pxt-colors-green-hover": "#1e8449", + "pxt-colors-green-alpha10": "#27ae6019", + + "pxt-colors-red-background": "#e74c3c", + "pxt-colors-red-foreground": "#ffffff", + "pxt-colors-red-hover": "#c0392b", + "pxt-colors-red-alpha10": "#e74c3c19", + + "pxt-colors-teal-background": "#1abc9c", + "pxt-colors-teal-foreground": "#ffffff", + "pxt-colors-teal-hover": "#16a085", + "pxt-colors-teal-alpha10": "#1abc9c19", + + "pxt-colors-yellow-background": "#fde300", + "pxt-colors-yellow-foreground": "#000000", + "pxt-colors-yellow-hover": "#e4cc00", + "pxt-colors-yellow-alpha10": "#fde30019" + } +} diff --git a/theme/color-themes/microbit-light.json b/theme/color-themes/microbit-light.json new file mode 100644 index 00000000000..100620a2167 --- /dev/null +++ b/theme/color-themes/microbit-light.json @@ -0,0 +1,136 @@ +{ + "id": "microbit-light", + "name": "Micro:bit Light", + "weight": 20, + "overrideFiles": [ + "/overrides/microbit-light-overrides.css" + ], + "colors": { + "pxt-header-background": "#3454D1", + "pxt-header-foreground": "#FFFFFF", + "pxt-header-background-hover": "#1d3282", + "pxt-header-foreground-hover": "#FFFFFF", + "pxt-header-stencil": "#2742ab", + + "pxt-primary-background": "#6633cc", + "pxt-primary-foreground": "#FFFFFF", + "pxt-primary-background-hover": "#5C2EB8", + "pxt-primary-foreground-hover": "#FFFFFF", + "pxt-primary-accent": "#5229A3", + + "pxt-secondary-background": "#3454D1", + "pxt-secondary-foreground": "#FFFFFF", + "pxt-secondary-background-hover": "#2742ab", + "pxt-secondary-foreground-hover": "#FFFFFF", + "pxt-secondary-accent": "#516DD8", + + "pxt-tertiary-background": "#3454D1", + "pxt-tertiary-foreground": "#FFFFFF", + "pxt-tertiary-background-hover": "#2742ab", + "pxt-tertiary-foreground-hover": "#FFFFFF", + "pxt-tertiary-accent": "#1d3282", + + "pxt-target-background1": "#ECF0F1", + "pxt-target-foreground1": "#000000", + "pxt-target-background1-hover": "#cfd9db", + "pxt-target-foreground1-hover": "#000000", + "pxt-target-stencil1": "#e1e1e1", + + "pxt-target-background2": "#FDFDFF", + "pxt-target-foreground2": "#000000", + "pxt-target-background2-hover": "#cacaff", + "pxt-target-foreground2-hover": "#000000", + "pxt-target-stencil2": "#e1e1e1", + + "pxt-target-background3": "#FFFFFF", + "pxt-target-foreground3": "#000000", + "pxt-target-background3-hover": "#e6e6e6", + "pxt-target-foreground3-hover": "#000000", + "pxt-target-stencil3": "#e1e1e1", + + "pxt-neutral-background1": "#FFFFFF", + "pxt-neutral-foreground1": "rgba(0,0,0,.85)", + "pxt-neutral-background1-hover": "#e6e6e6", + "pxt-neutral-foreground1-hover": "rgba(0,0,0,.85)", + "pxt-neutral-stencil1": "rgba(34, 74, 114, 0.15)", + + "pxt-neutral-background2": "#F8F8F8", + "pxt-neutral-foreground2": "rgba(0,0,0,.85)", + "pxt-neutral-background2-hover": "#DFDFDF", + "pxt-neutral-foreground2-hover": "rgba(0,0,0,.85)", + "pxt-neutral-stencil2": "#e9eef2", + + "pxt-neutral-background3": "#617374", + "pxt-neutral-foreground3": "#FFFFFF", + "pxt-neutral-background3-hover": "#363c3d", + "pxt-neutral-foreground3-hover": "#FFFFFF", + "pxt-neutral-stencil3": "#FFFFFF", + "pxt-neutral-background3-alpha90": "#617374E5", + + "pxt-neutral-base": "rgba(0, 0, 0, 1)", + "pxt-neutral-alpha0": "rgba(0, 0, 0, 0)", + "pxt-neutral-alpha10": "rgba(0, 0, 0, 0.1)", + "pxt-neutral-alpha20": "rgba(0, 0, 0, 0.2)", + "pxt-neutral-alpha50": "rgba(0, 0, 0, 0.5)", + "pxt-neutral-alpha80": "rgba(0, 0, 0, 0.8)", + + "pxt-link": "#3977B4", + "pxt-link-hover": "#204467", + "pxt-focus-border": "#0078D4", + + "pxt-success": "#2ECC71", + "pxt-pxt-success-foreground": "#000000", + "pxt-pxt-success-hover": "#22BE64", + "pxt-pxt-success-alpha10": "#2ECC7119", + + "pxt-warning": "#FFD43A", + "pxt-warning-foreground": "#000000", + "pxt-warning-hover": "#FFCE21", + "pxt-warning-alpha10": "#FFD43A19", + + "pxt-error": "#FF3A54", + "pxt-error-foreground": "#000000", + "pxt-error-hover": "#FF213E", + "pxt-error-alpha10": "#FF3A5419", + + "pxt-colors-purple-background": "#9932cc", + "pxt-colors-purple-foreground": "#FFFFFF", + "pxt-colors-purple-hover": "#7a28a3", + "pxt-colors-purple-alpha10": "#9932cc19", + + "pxt-colors-orange-background": "#ff7f50", + "pxt-colors-orange-foreground": "#FFFFFF", + "pxt-colors-orange-hover": "#ff5a1d", + "pxt-colors-orange-alpha10": "#ff7f5019", + + "pxt-colors-brown-background": "#663905", + "pxt-colors-brown-foreground": "#FFFFFF", + "pxt-colors-brown-hover": "#351e03", + "pxt-colors-brown-alpha10": "#66390519", + + "pxt-colors-blue-background": "#3454D1", + "pxt-colors-blue-foreground": "#FFFFFF", + "pxt-colors-blue-hover": "#2742ab", + "pxt-colors-blue-alpha10": "#3454D119", + + "pxt-colors-green-background": "#107c10", + "pxt-colors-green-foreground": "#FFFFFF", + "pxt-colors-green-hover": "#096a09", + "pxt-colors-green-alpha10": "#107c1019", + + "pxt-colors-red-background": "#E41B21", + "pxt-colors-red-foreground": "#FFFFFF", + "pxt-colors-red-hover": "#d60f15", + "pxt-colors-red-alpha10": "#e41b2119", + + "pxt-colors-teal-background": "#2C7485", + "pxt-colors-teal-foreground": "#FFFFFF", + "pxt-colors-teal-hover": "#1f535f", + "pxt-colors-teal-alpha10": "#2C748519", + + "pxt-colors-yellow-background": "#FDE74C", + "pxt-colors-yellow-foreground": "#000000", + "pxt-colors-yellow-hover": "#fce01a", + "pxt-colors-yellow-alpha10": "#FDE74C19" + } +} diff --git a/theme/color-themes/overrides/microbit-dark-overrides.css b/theme/color-themes/overrides/microbit-dark-overrides.css new file mode 100644 index 00000000000..d3e1b167080 --- /dev/null +++ b/theme/color-themes/overrides/microbit-dark-overrides.css @@ -0,0 +1,94 @@ + + +#simulator .editor-sidebar .filemenu { + --pxt-focus-border: yellow; +} + +#langmodal #availablelocales .langoption .header { + /* Better contrast than default, which is purple */ + color: var(--pxt-neutral-foreground1); +} + +.pxtToolbox span.blocklyTreeLabel, +.pxtToolbox .blocklyTreeSelected span.blocklyTreeLabel, +.pxtToolbox .blocklyTreeSelected .blocklyTreeIcon { + /* Better contrast in toolbox */ + color: var(--pxt-target-foreground3); +} + +.pxtToolbox #advanced > .blocklyTreeRow { + /* Better contrast for advanced section color */ + border-color: var(--pxt-neutral-alpha80); +} +.pxtToolbox #advanced > .blocklyTreeRow .blocklyTreeIcon { + /* Better contrast for advanced section arrow icon */ + color: var(--pxt-neutral-alpha80); +} + +.pxtToolbox #serial .blocklyTreeIcon, +.tutorial-container .serial span.docs.inlineblock { + /* Better contrast in toolbox & tutorial block colors, but try to preserve some of the icon color */ + filter: brightness(1.2) saturate(2); +} + +.pxtToolbox #control .blocklyTreeIcon, +.tutorial-container .control span.docs.inlineblock { + /* Better contrast in toolbox & tutorial block colors, but try to preserve some of the icon color */ + filter: brightness(1.2) saturate(2); +} + +#mainmenu { + /* Some parts of the app use the same color as a background. This keeps the menu from blending in */ + border-bottom: 1px solid var(--pxt-header-stencil); +} + +.projectsdialog .ui.card:hover { + /* Neutral and target colors are the same in dark theme, so it helps to have a clearer border when hovering over cards. */ + border-color: var(--pxt-focus-border) !important; +} + +#simulator #editorSidebar.editor-sidebar { + /* Need a lighter background for sidebar to make the simulator stand out, since it's black on black otherwise */ + background-color: var(--pxt-target-background1); +} + +.fullscreensim #boardview { + /* Gradient background is a little too intense in dark mode. Use a solid color instead */ + background: var(--pxt-target-background2); +} + +/* + * Inverted image colors + */ +.barcharticon, +.blockly-ws-search-next-btn, +.blockly-ws-search-previous-btn, +.blockly-ws-search-close-btn { + filter: invert(1); +} + +.modals .ui.button.immersive-reader-button, +#mainmenu .immersive-reader-button.ui.item, +#simulator .editor-sidebar .immersive-reader-button.ui.item { + background-image: url("/static/icons/immersive-reader-light.svg") !important; +} + +.carouselarrow { + /* Better contrast, especially against images in carousels */ + opacity: 0.9; +} + +/* For inverted buttons, it almost always looks better to have the background be dark instead of light (even though non-inverted has a light foreground) */ +button.ui.button.inverted:not(.teaching-bubble-button), +button.common-button.inverted:not(.teaching-bubble-button) { + background-color: var(--pxt-neutral-background2) !important; +} + +button.ui.button.inverted:hover:not(.teaching-bubble-button), +button.common-button.inverted:hover:not(.teaching-bubble-button) { + background-color: var(--pxt-neutral-background2-hover) !important; +} + +table.diffview.update .diff-added .ch-added { + color: var(--pxt-neutral-background1) !important; +} \ No newline at end of file diff --git a/theme/color-themes/overrides/microbit-light-overrides.css b/theme/color-themes/overrides/microbit-light-overrides.css new file mode 100644 index 00000000000..ea1d8cad07d --- /dev/null +++ b/theme/color-themes/overrides/microbit-light-overrides.css @@ -0,0 +1,61 @@ +#simulator .editor-sidebar .filemenu { + --pxt-focus-border: yellow; +} + +/* Lots of specificity to override another !important rule */ +#simulator #editorSidebar .simtoolbar .ui.icon.tiny.buttons .ui.button.play-button.play .icon.play { + color: var(--pxt-colors-green-background) !important; +} + +.theme-preview-microbit-light .theme-preview-sim-button { + background-color: var(--pxt-neutral-background2) !important; +} + +#filelist, #editortools { + /* Special textured backgrounds, but we only have assets for light theme */ + background: #fff url("/static/logo_texture.png") 0 0 repeat !important; +} + +/* + * Adjustments to match the original micro:bit theme + */ +path.blocklyFlyoutBackground { + fill: #4b4949 !important; +} + +.monacoFlyout { + background: #4b4949 !important; +} + +#simulator .editor-sidebar .filemenu { + background: var(--pxt-secondary-background); + color: var(--pxt-secondary-foreground); +} + +#simulator .editor-sidebar .filemenu .item:hover, #simulator .editor-sidebar .filemenu .link.item:hover { + background: var(--pxt-secondary-background-hover) !important; + color: var(--pxt-secondary-foreground-hover) !important; +} + +#simulator .ui.button.play-button .icon.play { + color: var(--pxt-colors-green-background) !important; +} + +.simtoolbar .ui.button.icon { + background-color: #e0e1e2; + color: rgba(0,0,0,.6); +} + +.simtoolbar .ui.button.icon:hover { + filter: none; + background-color: rgb(224, 225, 226); +} + +#serialPreview .label { + border-color: var(--pxt-primary-background); +} + +.ui.dimmer { + /* Matches color specified in dimmer.variables, but with partial transparency */ + background-color: rgba(52, 84, 209, 0.4) !important; +} \ No newline at end of file diff --git a/theme/site/globals/site.variables b/theme/site/globals/site.variables index 7396da0d605..58e400f40d0 100755 --- a/theme/site/globals/site.variables +++ b/theme/site/globals/site.variables @@ -9,7 +9,7 @@ @primaryColor: @purple; -@teal: #3891A6; +@teal: #2C7485; @blue: #3454D1; @red: #E41B21; @pink: #F46197; @@ -32,7 +32,7 @@ --------------------*/ @mainMenuInvertedBackground: @blue; -@mainMenuTutorialBackground: @orange; +@mainMenuTutorialBackground: @purple; @mainMenuBlocksJsToggleColor: @primaryColor; @@ -53,6 +53,7 @@ @simulatorBackground: #FDFDFF; @editorToolsBackground: @simulatorBackground; @blocklySvgColor: #ecf0f1; +@cloudCardBackground: lighten(@blue, 7); /*------------------- Side Docs @@ -84,4 +85,26 @@ --------------------*/ @serialTextColor: black; -@serialGraphBackground: #d9d9d9; \ No newline at end of file +@serialGraphBackground: #d9d9d9; + +/*--------------------------- + Tutorial +----------------------------*/ +@tutorialCodeValidationCorrectBackground: #cd0365; +@sidebarPrimaryColor: @primaryColor; +@sidebarSecondaryColor: @white; +@sidbarActiveTabIconColor: @primaryColor; +@tutorialBarBackgroundColor: #ecf0f1; +@tutorialSimframeMargin: 0.3rem; + +/*--------------------------- + Cloud Status +----------------------------*/ +@cloudStatusColor: @blue; + +/*--------------------------- + Teaching Bubble +----------------------------*/ +@teachingBubbleBackgroundColor: @blue; +@teachingBubbleTextColor: @white; +@teachingBubbleStepsColor: @lightGrey; diff --git a/theme/site/modules/dimmer.variables b/theme/site/modules/dimmer.variables index 3b5012b7fe9..affdc36901c 100755 --- a/theme/site/modules/dimmer.variables +++ b/theme/site/modules/dimmer.variables @@ -2,4 +2,4 @@ User Variable Overrides *******************************/ -@backgroundColor: fade(@blue, 40%); \ No newline at end of file +@backgroundColor: #B4BFED; \ No newline at end of file diff --git a/theme/style.less b/theme/style.less index 0b80f7c8799..cdfaec0f6d4 100644 --- a/theme/style.less +++ b/theme/style.less @@ -1,6 +1,7 @@ /* Import all components */ @import 'pxtsemantic'; @import 'pxt'; +@import 'themepacks'; @import 'blockly-toolbox'; @import 'themes/default/globals/site.variables'; @import 'themes/pxt/globals/site.variables'; @@ -21,53 +22,141 @@ src: @RobotoFont format("woff"); } +:root, .pxt-theme-root { + --pxt-page-font: @pageFont; +} + .ui.button.download-button { - &:extend(.ui.purple.button all); + background-color: var(--pxt-primary-background); + color: var(--pxt-primary-foreground); } .ui.button.hw-button { - background-color: darken(@purple, 10%); + background-color: var(--pxt-primary-accent) !important; + color: var(--pxt-primary-foreground) !important; } .docs.inlinebutton.ui.button.download-button:hover { - &:extend(.ui.purple.button all); + background-color: var(--pxt-primary-background); + color: var(--pxt-primary-foreground); } -.ui.button.play-button.play-button-full { - &:extend(.ui.inverted.button all); +#editortools .ui.button.editortools-btn, .simtoolbar .ui.button.icon { + background-color: var(--pxt-secondary-background); + color: var(--pxt-secondary-foreground); + + &:hover { + background-color: var(--pxt-secondary-background-hover); + color: var(--pxt-secondary-foreground-hover); + } } -.ui.button.getting-started-btn { - &:extend(.ui.orange.button all); +.main:not(.hc) { + .simtoolbar .ui.button.icon, #editortools .ui.button.editortools-btn { + &:hover { + filter: none; + } + } } -.ui.button.editortools-btn { - &:extend(.ui.blue.button all); +#simulator .ui.button.play-button .icon.play { + color: var(--pxt-secondary-foreground) !important; } -.ui.button.exit-tutorial-btn { - &:extend(.ui.blue.button all); +#downloadArea { + background: transparent !important; } -#filelist, #editortools { - background: #fff data-uri("../docs/static/logo_texture.png") 0 0 repeat !important; +#homescreen .ui.home { + .detailview .actions .card-action { + .button.approve { + background-color: @mainMenuTutorialBackground; + } + } + .tutorial-progress.orange { + background-color: @mainMenuTutorialBackground !important; + border-color: @mainMenuTutorialBackground !important; + } } -#downloadArea { - background: transparent !important; +/* Ensure project card timestamp fits within the card on tablet-width screens (e.g., iPad portrait) */ +@media only screen and (max-width: @largestTabletScreen) { + .projectsdialog .ui.card.file .meta { + font-size: 0.7rem; + padding: 0.5rem; + } } -.ui.home { - .getting-started-segment { - background-position: 50% 25%; +#tutorialcard { + .ui.button.orange.right.attached { + background-color: @mainMenuTutorialBackground; + &:focus, &:hover { + background-color: darken(@mainMenuTutorialBackground, 20%); + } } - .detailview .actions .card-action { - &:first-child .button.approve { - background-color: lighten(@primaryColor, 10%); +} + +.time-machine-header { + background-color: @blue; +} + +.time-machine-preview { + // Loading div that appears while projects are importing + & > div { + background: #e8e8e8 + } +} + +#root:not(.hc) .tutorial-menu { + .ui.circular.label.selected { + background-color: @purple !important; + color: #e8e8e8 !important; + border: 1px solid #e8e8e8 !important; + + &:focus, &:hover { + background-color: darken(@purple, 20%) !important; } } } +.menubar { + .ui.menu .brand .ui.logo { + height: 1.1rem; + } +} + +.sound-effect-header { + background-color: rgb(230, 48, 34); // Match block color +} + + +.ui.black.microbit-ribbon.label { + position: absolute; + border-radius: 0; + margin: 0; + right: 0; + width: 45%; + height: 25%; + &::before { + content: ""; + background-image: data-uri("../docs/static/logo.portrait.white.svg"); + background-position: center; + background-repeat: no-repeat; + background-size: contain; + position: absolute; + left: 50%; + top: 50%; + right: auto; + bottom: auto; + display: block; + width: 88%; + height: 76%; + max-width: 6.75rem; + max-height: 1.2rem; + transform: translate(-50%, -50%); + } +} + /* Mobile */ @media only screen and (max-width: @largestMobileScreen) { #filelist { @@ -76,6 +165,14 @@ #blocklySearchArea { display: none !important; } + .ui.black.microbit-ribbon.label { + width: 50%; + height: 25%; + &::before { + max-width: 4.9rem; + max-height: 0.875rem; + } + } } /* Tablet */ @@ -83,6 +180,14 @@ #filelist { background: transparent !important; } + .ui.black.microbit-ribbon.label { + width: 55%; + height: 20%; + &::before { + max-width: 5.5rem; + max-height: 1rem; + } + } } /* Small Monitor */ @@ -106,15 +211,15 @@ justify-content: center; display: flex; padding: 1rem; - + img { - height:100px; + height:100px; } } } .instructions { img { - margin-bottom:1rem; + margin-bottom:1rem; } } } \ No newline at end of file diff --git a/theme/theme.config b/theme/theme.config index 0e68dd0672c..1679d89aa62 100644 --- a/theme/theme.config +++ b/theme/theme.config @@ -17,6 +17,8 @@ specify theme name below */ +@placeholder: 'default'; + /* Global */ @site : 'pxt'; @reset : 'default'; @@ -87,7 +89,7 @@ Import Theme *******************************/ -@import "theme.less"; +@import (multiple) "theme.less"; @fontPath : 'fonts'; diff --git a/webmanifest.json b/webmanifest.json index 68b81b56eab..9165a1515a5 100644 --- a/webmanifest.json +++ b/webmanifest.json @@ -1,59 +1,59 @@ { "name": "makecode.microbit.org", - "display": "fullscreen", - "orientation": "portrait", "icons": [ { - "src": "./static/icons/android-chrome-36x36.png", + "src": "/static/icons/android-chrome-36x36.png", "sizes": "36x36", "type": "image\/png" }, { - "src": "./static/icons/android-chrome-48x48.png", + "src": "/static/icons/android-chrome-48x48.png", "sizes": "48x48", "type": "image\/png" }, { - "src": "./static/icons/android-chrome-72x72.png", + "src": "/static/icons/android-chrome-72x72.png", "sizes": "72x72", "type": "image\/png" }, { - "src": "./static/icons/android-chrome-96x96.png", + "src": "/static/icons/android-chrome-96x96.png", "sizes": "96x96", "type": "image\/png" }, { - "src": "./static/icons/android-chrome-144x144.png", + "src": "/static/icons/android-chrome-144x144.png", "sizes": "144x144", "type": "image\/png" }, { - "src": "./static/icons/android-chrome-192x192.png", + "src": "/static/icons/android-chrome-192x192.png", "sizes": "192x192", "type": "image\/png" }, { - "src": "./static/icons/android-chrome-256x256.png", + "src": "/static/icons/android-chrome-256x256.png", "sizes": "256x256", "type": "image\/png" }, { - "src": "./static/icons/android-chrome-384x384.png", + "src": "/static/icons/android-chrome-384x384.png", "sizes": "384x384", "type": "image\/png" }, { - "src": "./static/icons/android-chrome-512x512.png", + "src": "/static/icons/android-chrome-512x512.png", "sizes": "512x512", "type": "image\/png" + }, + { + "src": "/static/icons/maskable-icon-640x640.png", + "sizes": "640x640", + "type": "image\/png", + "purpose": "maskable" } ], "theme_color": "#ffffff", "related_applications": [ - { - "platform": "play", - "id": "com.samsung.microbit" - } ] -} \ No newline at end of file +}