diff --git a/Taskfile.yaml b/Taskfile.yaml index 7d61e13..19e0143 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -27,10 +27,12 @@ tasks: # copy the extension to the rigth place - ln -s $PWD/scratch-prg-extensions/extensions/src/arduino_basics $PWD/prg-raise-playground/extensions/src/arduino_basics - cd scratch-prg-extensions/extensions/src/arduino_basics && pnpm install + - ln -s $PWD/scratch-prg-extensions/extensions/src/arduino_modulino $PWD/prg-raise-playground/extensions/src/arduino_modulino + - cd scratch-prg-extensions/extensions/src/arduino_modulino && pnpm install scratch:watch: cmds: - - cd prg-raise-playground && pnpm dev -i arduino_basics + - cd prg-raise-playground && pnpm dev -i arduino_basics arduino_modulino app:build: desc: "Copy app files (python, assets, app.yaml) to a build directory" diff --git a/python/main.py b/python/main.py index 40ffc89..04a8545 100644 --- a/python/main.py +++ b/python/main.py @@ -13,4 +13,11 @@ def on_matrix_draw(_, data): ui.on_message("matrix_draw", on_matrix_draw) + +def on_modulino_button_pressed(btn): + ui.send_message("modulino_buttons_pressed", {"btn": btn}) + + +Bridge.provide("modulino_button_pressed", on_modulino_button_pressed) + App.run() diff --git a/scratch-prg-extensions/extensions/src/arduino_basics/MatrixArgument.svelte b/scratch-prg-extensions/extensions/src/arduino_basics/MatrixArgument.svelte index 768c919..c3452e6 100644 --- a/scratch-prg-extensions/extensions/src/arduino_basics/MatrixArgument.svelte +++ b/scratch-prg-extensions/extensions/src/arduino_basics/MatrixArgument.svelte @@ -71,8 +71,7 @@ @@ -147,8 +168,8 @@
0 ? `rgba(0, 123, 255)` : '#222'} - style:box-shadow={ledValue > 0 ? `0 0 ${ledValue * 2}px rgba(0, 123, 255, 0.8)` : 'none'} + style:background-color={ledValue > 0 ? `#FFFFFF` : '#62AEB2'} + style:box-shadow={ledValue > 0 ? `0 0 ${ledValue}px rgba(255, 255, 255, 0.7)` : 'none'} on:mousedown={(e) => handleMouseDown(e, rowIndex, colIndex)} on:mouseenter={() => handleMouseEnter(rowIndex, colIndex)} on:contextmenu={handleContextMenu} @@ -162,7 +183,31 @@
- - + +
\ No newline at end of file diff --git a/scratch-prg-extensions/extensions/src/arduino_basics/index.ts b/scratch-prg-extensions/extensions/src/arduino_basics/index.ts index bf8b9ad..027b5d8 100644 --- a/scratch-prg-extensions/extensions/src/arduino_basics/index.ts +++ b/scratch-prg-extensions/extensions/src/arduino_basics/index.ts @@ -3,13 +3,13 @@ import { io, Socket } from "socket.io-client"; import MatrixArgument from "./MatrixArgument.svelte"; const details: ExtensionMenuDisplayDetails = { - name: "Arduino Basics", - description: "Arduino Basics for Uno Q", - iconURL: "ArduinoLogo_Blue.png", - insetIconURL: "ArduinoLogo_Blue.jpg", - tags: ["Arduino"], + name: "Basic", + description: "Play with UNO Q matrix, leds and pins", + iconURL: "matrix.png", + insetIconURL: "unoq.svg", + tags: ["Arduino UNO Q"], blockColor: "#00878F", - menuColor: "#62AEB2", + menuColor: "#8C7965", menuSelectColor: "#62AEB2", }; diff --git a/scratch-prg-extensions/extensions/src/arduino_basics/matrix.png b/scratch-prg-extensions/extensions/src/arduino_basics/matrix.png new file mode 100644 index 0000000..dfbc054 Binary files /dev/null and b/scratch-prg-extensions/extensions/src/arduino_basics/matrix.png differ diff --git a/scratch-prg-extensions/extensions/src/arduino_basics/unoq.svg b/scratch-prg-extensions/extensions/src/arduino_basics/unoq.svg new file mode 100644 index 0000000..ac92aad --- /dev/null +++ b/scratch-prg-extensions/extensions/src/arduino_basics/unoq.svg @@ -0,0 +1,1460 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scratch-prg-extensions/extensions/src/arduino_modulino/.gitignore b/scratch-prg-extensions/extensions/src/arduino_modulino/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/scratch-prg-extensions/extensions/src/arduino_modulino/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/scratch-prg-extensions/extensions/src/arduino_modulino/ButtonArgument.svelte b/scratch-prg-extensions/extensions/src/arduino_modulino/ButtonArgument.svelte new file mode 100644 index 0000000..96101f3 --- /dev/null +++ b/scratch-prg-extensions/extensions/src/arduino_modulino/ButtonArgument.svelte @@ -0,0 +1,231 @@ + + + + +
+
+
+
+
+
+ +
+
+ +
+
+
handleButtonClick('A')} + on:keydown={(e) => e.key === 'Enter' && handleButtonClick('A')} + role="button" + tabindex="0" + title="Button A" + >
+
A
+
+
+
handleButtonClick('B')} + on:keydown={(e) => e.key === 'Enter' && handleButtonClick('B')} + role="button" + tabindex="0" + title="Button B" + >
+
B
+
+
+
handleButtonClick('C')} + on:keydown={(e) => e.key === 'Enter' && handleButtonClick('C')} + role="button" + tabindex="0" + title="Button C" + >
+
C
+
+
+
+
diff --git a/scratch-prg-extensions/extensions/src/arduino_modulino/index.test.ts b/scratch-prg-extensions/extensions/src/arduino_modulino/index.test.ts new file mode 100644 index 0000000..fccb213 --- /dev/null +++ b/scratch-prg-extensions/extensions/src/arduino_modulino/index.test.ts @@ -0,0 +1,7 @@ +import { createTestSuite } from "$testing"; +import Extension from "."; + +createTestSuite({ Extension, __dirname }, { + unitTests: undefined, + integrationTests: undefined, +}); diff --git a/scratch-prg-extensions/extensions/src/arduino_modulino/index.ts b/scratch-prg-extensions/extensions/src/arduino_modulino/index.ts new file mode 100644 index 0000000..8dc8d69 --- /dev/null +++ b/scratch-prg-extensions/extensions/src/arduino_modulino/index.ts @@ -0,0 +1,75 @@ +import { + type BlockUtilityWithID, + type Environment, + extension, + type ExtensionMenuDisplayDetails, + scratch, +} from "$common"; +import { io, type Socket } from "socket.io-client"; +import ButtonArgument from "./ButtonArgument.svelte"; + +// Get Arduino board IP or hostname from URL parameter +const getArduinoBoardHost = () => { + const urlParams = new URLSearchParams(window.location.search); + const boardHost = urlParams.get("host"); + if (boardHost) { + return boardHost; + } + return window.location.hostname; +}; + +export default class ModulinoButtons extends extension({ + name: "Modulino", + description: "Control your Arduino Modulinos", + iconURL: "modulinos.png", // png + insetIconURL: "modulino-buttons.svg", // svg + tags: ["Arduino UNO Q"], + blockColor: "#00878F", + menuColor: "#8C7965", + menuSelectColor: "#62AEB2", +}, "customArguments") { + private socket: Socket | null = null; + private button_pressed: string = ""; + + init(env: Environment) { + const arduinoBoardHost = getArduinoBoardHost(); + var serverURL = `wss://${arduinoBoardHost}:7000`; + + this.socket = io(serverURL, { + path: "/socket.io", + transports: ["polling", "websocket"], + autoConnect: true, + }); + + this.socket.on("connect", () => { + console.log(`Connected to Arduino UNO Q`, serverURL); + }); + + this.socket.on("disconnect", (reason) => { + console.log(`Disconnected from Arduino UNO Q: ${reason}`); + }); + + this.socket.on("modulino_buttons_pressed", (data) => { + console.log(`Modulino button pressed event received: ${data.btn}`); + this.button_pressed = data.btn.toUpperCase(); + }); + } + + @scratch.hat(function(instance, tag) { + const arg = instance.makeCustomArgument({ + component: ButtonArgument, + initial: { + value: "A", + text: "A", + }, + }); + return tag`When modulino button ${arg} pressed`; + }) + whenModulinoButtonsPressed(button: string, util: BlockUtilityWithID) { + if (button.toUpperCase() === this.button_pressed) { + this.button_pressed = ""; + return true; + } + return false; + } +} diff --git a/scratch-prg-extensions/extensions/src/arduino_modulino/modulino-buttons.svg b/scratch-prg-extensions/extensions/src/arduino_modulino/modulino-buttons.svg new file mode 100644 index 0000000..7b8cb18 --- /dev/null +++ b/scratch-prg-extensions/extensions/src/arduino_modulino/modulino-buttons.svg @@ -0,0 +1,1340 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scratch-prg-extensions/extensions/src/arduino_modulino/modulinos.png b/scratch-prg-extensions/extensions/src/arduino_modulino/modulinos.png new file mode 100644 index 0000000..b317723 Binary files /dev/null and b/scratch-prg-extensions/extensions/src/arduino_modulino/modulinos.png differ diff --git a/scratch-prg-extensions/extensions/src/arduino_modulino/package.json b/scratch-prg-extensions/extensions/src/arduino_modulino/package.json new file mode 100644 index 0000000..1ff6031 --- /dev/null +++ b/scratch-prg-extensions/extensions/src/arduino_modulino/package.json @@ -0,0 +1,18 @@ +{ + "name": "arduino_modulino-extension", + "version": "1.0.0", + "description": "An extension created using the PRG AI Blocks framework", + "main": "index.ts", + "scripts": { + "directory": "echo arduino_modulino", + "test": "pnpm --filter prg-extension-root test arduino_modulino/index.test.ts", + "dev": "pnpm --filter prg-extension-root dev --include arduino_modulino", + "add:ui": "pnpm --filter prg-extension-root add:ui arduino_modulino", + "add:arg": "pnpm --filter prg-extension-root add:arg arduino_modulino" + }, + "author": "", + "license": "ISC", + "dependencies": { + "socket.io-client": "^4.8.1" + } +} diff --git a/scratch-prg-extensions/extensions/src/arduino_modulino/pnpm-lock.yaml b/scratch-prg-extensions/extensions/src/arduino_modulino/pnpm-lock.yaml new file mode 100644 index 0000000..649d689 --- /dev/null +++ b/scratch-prg-extensions/extensions/src/arduino_modulino/pnpm-lock.yaml @@ -0,0 +1,122 @@ +lockfileVersion: "9.0" + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + .: + dependencies: + socket.io-client: + specifier: ^4.8.1 + version: 4.8.1 + +packages: + "@socket.io/component-emitter@3.1.2": + resolution: { + integrity: sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==, + } + + debug@4.3.7: + resolution: { + integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==, + } + engines: { node: ">=6.0" } + peerDependencies: + supports-color: "*" + peerDependenciesMeta: + supports-color: + optional: true + + engine.io-client@6.6.3: + resolution: { + integrity: sha512-T0iLjnyNWahNyv/lcjS2y4oE358tVS/SYQNxYXGAJ9/GLgH4VCvOQ/mhTjqU88mLZCQgiG8RIegFHYCdVC+j5w==, + } + + engine.io-parser@5.2.3: + resolution: { + integrity: sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==, + } + engines: { node: ">=10.0.0" } + + ms@2.1.3: + resolution: { + integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, + } + + socket.io-client@4.8.1: + resolution: { + integrity: sha512-hJVXfu3E28NmzGk8o1sHhN3om52tRvwYeidbj7xKy2eIIse5IoKX3USlS6Tqt3BHAtflLIkCQBkzVrEEfWUyYQ==, + } + engines: { node: ">=10.0.0" } + + socket.io-parser@4.2.4: + resolution: { + integrity: sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==, + } + engines: { node: ">=10.0.0" } + + ws@8.17.1: + resolution: { + integrity: sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==, + } + engines: { node: ">=10.0.0" } + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xmlhttprequest-ssl@2.1.2: + resolution: { + integrity: sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==, + } + engines: { node: ">=0.4.0" } + +snapshots: + "@socket.io/component-emitter@3.1.2": {} + + debug@4.3.7: + dependencies: + ms: 2.1.3 + + engine.io-client@6.6.3: + dependencies: + "@socket.io/component-emitter": 3.1.2 + debug: 4.3.7 + engine.io-parser: 5.2.3 + ws: 8.17.1 + xmlhttprequest-ssl: 2.1.2 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + engine.io-parser@5.2.3: {} + + ms@2.1.3: {} + + socket.io-client@4.8.1: + dependencies: + "@socket.io/component-emitter": 3.1.2 + debug: 4.3.7 + engine.io-client: 6.6.3 + socket.io-parser: 4.2.4 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + + socket.io-parser@4.2.4: + dependencies: + "@socket.io/component-emitter": 3.1.2 + debug: 4.3.7 + transitivePeerDependencies: + - supports-color + + ws@8.17.1: {} + + xmlhttprequest-ssl@2.1.2: {}