From 7ad1c6552a3d1fd1e889246116b910745ca4565a Mon Sep 17 00:00:00 2001 From: dido18 Date: Sat, 29 Nov 2025 22:41:10 +0100 Subject: [PATCH 1/2] feat: Add Include Robot extension and enhance robot control functionality --- Taskfile.yaml | 10 +- python/main.py | 1 + .../src/arduino_include_robot/.gitignore | 1 + .../src/arduino_include_robot/index.test.ts | 7 ++ .../src/arduino_include_robot/index.ts | 57 ++++++++++ .../src/arduino_include_robot/package.json | 18 +++ .../src/arduino_include_robot/pnpm-lock.yaml | 107 ++++++++++++++++++ sketch/robot.cpp | 48 ++++++++ sketch/robot.h | 26 +++++ sketch/sketch.ino | 10 ++ sketch/sketch.yaml | 1 + 11 files changed, 282 insertions(+), 4 deletions(-) create mode 100644 scratch-prg-extensions/extensions/src/arduino_include_robot/.gitignore create mode 100644 scratch-prg-extensions/extensions/src/arduino_include_robot/index.test.ts create mode 100644 scratch-prg-extensions/extensions/src/arduino_include_robot/index.ts create mode 100644 scratch-prg-extensions/extensions/src/arduino_include_robot/package.json create mode 100644 scratch-prg-extensions/extensions/src/arduino_include_robot/pnpm-lock.yaml create mode 100644 sketch/robot.cpp create mode 100644 sketch/robot.h diff --git a/Taskfile.yaml b/Taskfile.yaml index 342d264..07e7bf2 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -20,15 +20,17 @@ tasks: - ${PWD}/.bin/dprint check scratch:init: - - git config --global url."https://github.com/mitmedialab/prg-raise-playground-scratch-gui.git".insteadOf "git@github.com:mitmedialab/prg-raise-playground-scratch-gui.git" - - git config --global url."https://github.com/mitmedialab/prg-raise-playground-scratch-vm.git".insteadOf "git@github.com:mitmedialab/prg-raise-playground-scratch-vm.git" - - git clone --recurse-submodules https://github.com/mitmedialab/prg-raise-playground.git + # - git config --global url."https://github.com/mitmedialab/prg-raise-playground-scratch-gui.git".insteadOf "git@github.com:mitmedialab/prg-raise-playground-scratch-gui.git" + # - git config --global url."https://github.com/mitmedialab/prg-raise-playground-scratch-vm.git".insteadOf "git@github.com:mitmedialab/prg-raise-playground-scratch-vm.git" + # - git clone --recurse-submodules https://github.com/mitmedialab/prg-raise-playground.git - cd prg-raise-playground && git switch dev && pnpm install # 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 + - ln -s $PWD/scratch-prg-extensions/extensions/src/arduino_include_robot $PWD/prg-raise-playground/extensions/src/arduino_include_robot + - cd scratch-prg-extensions/extensions/src/arduino_include_robot && pnpm install app:upload: desc: "Build,and Upload zip file to Arduino board, unzip and deploy to /home/arduino/ArduinoApps" @@ -84,7 +86,7 @@ tasks: scratch:watch: cmds: - - cd prg-raise-playground && pnpm dev -i arduino_basics arduino_modulino + - cd prg-raise-playground && pnpm dev -i arduino_basics arduino_modulino arduino_include_robot python:watch: desc: "Watch Python folder for changes and auto-upload/restart" diff --git a/python/main.py b/python/main.py index 04a8545..7cdbf94 100644 --- a/python/main.py +++ b/python/main.py @@ -12,6 +12,7 @@ def on_matrix_draw(_, data): ui.on_message("matrix_draw", on_matrix_draw) +ui.on_message("robot_forward_for_steps", lambda _, data: Bridge.call("robot_forward_for_steps", data.get("steps"))) def on_modulino_button_pressed(btn): diff --git a/scratch-prg-extensions/extensions/src/arduino_include_robot/.gitignore b/scratch-prg-extensions/extensions/src/arduino_include_robot/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/scratch-prg-extensions/extensions/src/arduino_include_robot/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/scratch-prg-extensions/extensions/src/arduino_include_robot/index.test.ts b/scratch-prg-extensions/extensions/src/arduino_include_robot/index.test.ts new file mode 100644 index 0000000..fccb213 --- /dev/null +++ b/scratch-prg-extensions/extensions/src/arduino_include_robot/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_include_robot/index.ts b/scratch-prg-extensions/extensions/src/arduino_include_robot/index.ts new file mode 100644 index 0000000..62af048 --- /dev/null +++ b/scratch-prg-extensions/extensions/src/arduino_include_robot/index.ts @@ -0,0 +1,57 @@ +import { + type Environment, + extension, + scratch +} from "$common"; + +// import { io, type Socket } from "socket.io-client"; + +// 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 IncludeRobot extends extension({ + name: "Include Robot", + description: "A programmable robot designed to teach junior students about robotics in a simple and funny way.", +// iconURL: "include-robot.png", // png +// insetIconURL: "include-robot-buttons.svg", // svg + tags: ["Arduino UNO Q"], + blockColor: "#00878F", + menuColor: "#8C7965", + menuSelectColor: "#62AEB2", +}) { +// private socket: Socket | null = null; + + 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}`); + // }); + } + + @(scratch.command`Move robot forward for ${"number"} steps`) + robotForwardForSteps(steps: number) { + if (this.socket) { + this.socket.emit("robot_forward_for_steps", { steps: steps }); + } + } + +} diff --git a/scratch-prg-extensions/extensions/src/arduino_include_robot/package.json b/scratch-prg-extensions/extensions/src/arduino_include_robot/package.json new file mode 100644 index 0000000..d8b5417 --- /dev/null +++ b/scratch-prg-extensions/extensions/src/arduino_include_robot/package.json @@ -0,0 +1,18 @@ +{ + "name": "arduino_include_robot-extension", + "version": "1.0.0", + "description": "An extension created using the PRG AI Blocks framework", + "main": "index.ts", + "scripts": { + "directory": "echo arduino_include_robot", + "test": "pnpm --filter prg-extension-root test arduino_include_robot/index.test.ts", + "dev": "pnpm --filter prg-extension-root dev --include arduino_include_robot", + "add:ui": "pnpm --filter prg-extension-root add:ui arduino_include_robot", + "add:arg": "pnpm --filter prg-extension-root add:arg arduino_include_robot" + }, + "author": "", + "license": "ISC", + "dependencies": { + "socket.io-client": "^4.8.1" + } +} diff --git a/scratch-prg-extensions/extensions/src/arduino_include_robot/pnpm-lock.yaml b/scratch-prg-extensions/extensions/src/arduino_include_robot/pnpm-lock.yaml new file mode 100644 index 0000000..43a4e08 --- /dev/null +++ b/scratch-prg-extensions/extensions/src/arduino_include_robot/pnpm-lock.yaml @@ -0,0 +1,107 @@ +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: {} diff --git a/sketch/robot.cpp b/sketch/robot.cpp new file mode 100644 index 0000000..a7ecda4 --- /dev/null +++ b/sketch/robot.cpp @@ -0,0 +1,48 @@ +#include "robot.h" +#include + + +void Robot::moveForward(const int steps, const uint16_t for_ms) { + for (int i = 0; i < steps; i++) { + this->rightWheel.write(90 + this->speed); + this->leftWheel.write(90 - this->speed); + delay(for_ms); + this->rightWheel.write(90); + this->leftWheel.write(90); + delay(500); + } +} + +void Robot::moveBackward(const int steps, const uint16_t for_ms) { + for (int i = 0; i < steps; i++) { + this->rightWheel.write(90 - this->speed); + this->leftWheel.write(90 + this->speed); + delay(for_ms); + this->rightWheel.write(90); + this->leftWheel.write(90); + delay(500); + } +} + +void Robot::turnRight(const uint16_t for_ms) { + this->rightWheel.write(90 + 10); + this->leftWheel.write(90 + 10); + delay(for_ms); + this->rightWheel.write(90); + this->leftWheel.write(90); +} + +void Robot::turnLeft(const uint16_t for_ms) { + this->rightWheel.write(90 - 10); + this->leftWheel.write(90 - 10); + delay(for_ms); + this->rightWheel.write(90); + this->leftWheel.write(90); +} + +// set the speed of the robot. From 0 (no speed) to 100 (max speed) +void Robot::setSpeed(int speed) { + speed = constrain(speed, 0, 100); + speed = map(speed, 0, 100, 0, 90); + this->speed = speed; +} diff --git a/sketch/robot.h b/sketch/robot.h new file mode 100644 index 0000000..efb1257 --- /dev/null +++ b/sketch/robot.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +class Robot { + Servo rightWheel; + Servo leftWheel; + int speed; + +public: + Robot() {} // Empty constructor + Robot(int rightWheelPin, int leftWheelPin) { + this->rightWheel = Servo(); + this->leftWheel = Servo(); + this->rightWheel.attach(rightWheelPin); + this->leftWheel.attach(leftWheelPin); + this->setSpeed(10); + } + + void setSpeed(const int steps); + void moveForward(const int steps, const uint16_t for_ms); + void moveBackward(const int steps, const uint16_t for_ms); + void turnRight(const uint16_t for_ms); + void turnLeft(const uint16_t for_ms); +}; diff --git a/sketch/sketch.ino b/sketch/sketch.ino index 0dac15f..e14d4da 100644 --- a/sketch/sketch.ino +++ b/sketch/sketch.ino @@ -1,10 +1,15 @@ #include #include "Arduino_LED_Matrix.h" #include +#include "robot.h" Arduino_LED_Matrix matrix; ModulinoButtons buttons; +uint8_t rservo = 9; +uint8_t lservo = 8; +Robot myra = Robot(rservo, lservo); + void setup() { matrix.begin(); Bridge.begin(); @@ -28,6 +33,11 @@ void setup() { buttons.setLeds(true, true, true); Bridge.provide("matrix_draw", matrix_draw); Bridge.provide("set_led_rgb", set_led_rgb); + Bridge.provide("robot_forward_for_steps", robot_forward_for_steps); +} + +void robot_forward_for_steps(uint8_t steps){ + myra.moveForward(steps, 1000); } void loop() { diff --git a/sketch/sketch.yaml b/sketch/sketch.yaml index 8024762..ce600fb 100644 --- a/sketch/sketch.yaml +++ b/sketch/sketch.yaml @@ -14,4 +14,5 @@ profiles: - STM32duino VL53L4ED (1.0.1) - Arduino_LSM6DSOX (1.1.2) - Arduino_LPS22HB (1.0.2) + - Servo (1.3.0) default_profile: default From 6c480fb3d8b11f1d271130e1804386f3a8b08308 Mon Sep 17 00:00:00 2001 From: dido18 Date: Sat, 29 Nov 2025 22:41:21 +0100 Subject: [PATCH 2/2] fix: Uncomment essential git configuration and clone commands in scratch:init task --- Taskfile.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Taskfile.yaml b/Taskfile.yaml index 07e7bf2..54b94fd 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -20,9 +20,9 @@ tasks: - ${PWD}/.bin/dprint check scratch:init: - # - git config --global url."https://github.com/mitmedialab/prg-raise-playground-scratch-gui.git".insteadOf "git@github.com:mitmedialab/prg-raise-playground-scratch-gui.git" - # - git config --global url."https://github.com/mitmedialab/prg-raise-playground-scratch-vm.git".insteadOf "git@github.com:mitmedialab/prg-raise-playground-scratch-vm.git" - # - git clone --recurse-submodules https://github.com/mitmedialab/prg-raise-playground.git + - git config --global url."https://github.com/mitmedialab/prg-raise-playground-scratch-gui.git".insteadOf "git@github.com:mitmedialab/prg-raise-playground-scratch-gui.git" + - git config --global url."https://github.com/mitmedialab/prg-raise-playground-scratch-vm.git".insteadOf "git@github.com:mitmedialab/prg-raise-playground-scratch-vm.git" + - git clone --recurse-submodules https://github.com/mitmedialab/prg-raise-playground.git - cd prg-raise-playground && git switch dev && pnpm install # 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