diff --git a/Taskfile.yaml b/Taskfile.yaml index 342d264..a0d5020 100644 --- a/Taskfile.yaml +++ b/Taskfile.yaml @@ -36,15 +36,15 @@ tasks: - task app:build - task app:zip - | - ZIP_FILE=$(ls -t build/scratch-arduino-app-*.zip 2>/dev/null | head -n1) - if [ -z "$ZIP_FILE" ]; then - echo "No zip file found. Run 'task app:zip' first." - exit 1 - fi - adb push "$ZIP_FILE" /tmp/ - ZIP_BASENAME=$(basename "$ZIP_FILE") - adb shell "cd /tmp && unzip -o $ZIP_BASENAME && mkdir -p /home/arduino/ArduinoApps && rm -rf /home/arduino/ArduinoApps/scratch-arduino-app && mv scratch-arduino-app /home/arduino/ArduinoApps/ && rm $ZIP_BASENAME" - echo "App deployed to /home/arduino/ArduinoApps/scratch-arduino-app" + ZIP_FILE=$(ls -t build/scratch-arduino-app-*.zip 2>/dev/null | head -n1) + if [ -z "$ZIP_FILE" ]; then + echo "No zip file found. Run 'task app:zip' first." + exit 1 + fi + adb push "$ZIP_FILE" /tmp/ + ZIP_BASENAME=$(basename "$ZIP_FILE") + adb shell "cd /tmp && unzip -o $ZIP_BASENAME && mkdir -p /home/arduino/ArduinoApps && rm -rf /home/arduino/ArduinoApps/scratch-arduino-app && mv scratch-arduino-app /home/arduino/ArduinoApps/ && rm $ZIP_BASENAME" + echo "App deployed to /home/arduino/ArduinoApps/scratch-arduino-app" - task app:start app:build: @@ -65,10 +65,10 @@ tasks: sh: echo "${APP_VERSION:-$(git rev-parse --short HEAD 2>/dev/null || echo 'unknown')}" cmds: - | - if [ ! -d "build/scratch-arduino-app" ]; then - echo "Build folder does not exist. Run 'task app:build' first." - exit 1 - fi + if [ ! -d "build/scratch-arduino-app" ]; then + echo "Build folder does not exist. Run 'task app:build' first." + exit 1 + fi - echo "Creating zip with version {{.APP_VERSION}}" - cd build && zip -r scratch-arduino-app-{{.APP_VERSION}}.zip scratch-arduino-app && cd .. @@ -91,69 +91,69 @@ tasks: silent: true cmds: - | - while true; do - echo "🐍 Waiting for changes in python folder..." - inotifywait -r -e modify,create,delete,move python/ 2>/dev/null || { - echo "inotifywait failed, retrying..." - sleep 2 - continue - } - - sleep 1 # Waiting 1 second for file writes to complete..." - echo "🐍 Change detected: uploading and restarting service..." - task python:upload-and-restart || { - echo "Upload/restart failed, continuing to watch..." - continue - } - - done + while true; do + echo "🐍 Waiting for changes in python folder..." + inotifywait -r -e modify,create,delete,move python/ 2>/dev/null || { + echo "inotifywait failed, retrying..." + sleep 2 + continue + } + + sleep 1 # Waiting 1 second for file writes to complete..." + echo "🐍 Change detected: uploading and restarting service..." + task python:upload-and-restart || { + echo "Upload/restart failed, continuing to watch..." + continue + } + + done python:upload-and-restart: desc: "Upload Python files and restart container" silent: true cmds: - | - adb push ./python/ /home/arduino/ArduinoApps/scratch-arduino-app/ + adb push ./python/ /home/arduino/ArduinoApps/scratch-arduino-app/ - CONTAINER_ID=$(adb shell "docker ps -q --filter 'ancestor=ghcr.io/arduino/app-bricks/python-apps-base:0.5.0'") - if [ -n "$CONTAINER_ID" ]; then - /usr/bin/time -f "🐍 Restarted in %es" adb shell "docker restart $CONTAINER_ID" - else - echo "[warning] No running container found with image ghcr.io/arduino/app-bricks/python-apps-base" - fi + CONTAINER_ID=$(adb shell "docker ps -q --filter 'ancestor=ghcr.io/arduino/app-bricks/python-apps-base:0.5.0'") + if [ -n "$CONTAINER_ID" ]; then + /usr/bin/time -f "🐍 Restarted in %es" adb shell "docker restart $CONTAINER_ID" + else + echo "[warning] No running container found with image ghcr.io/arduino/app-bricks/python-apps-base" + fi sketch:watch: desc: "Watch sketch folder for changes and auto-compile/upload" silent: true cmds: - | - while true; do - echo "♾️ Waiting for changes in sketch folder..." - inotifywait -r -e modify,create,delete sketch/ --include '\.(ino|cpp|h|c)$' 2>/dev/null || { - echo "inotifywait failed, retrying..." - sleep 2 - continue - } - - sleep 1 # Waiting 1 second for file writes to complete - echo "♾️ Change detected: compiling and uploading..." - task sketch:compile-and-upload|| { - echo "Compile/upload failed, continuing to watch..." - continue - } - echo - done + while true; do + echo "♾️ Waiting for changes in sketch folder..." + inotifywait -r -e modify,create,delete sketch/ --include '\.(ino|cpp|h|c|yaml)$' 2>/dev/null || { + echo "inotifywait failed, retrying..." + sleep 2 + continue + } + + sleep 1 # Waiting 1 second for file writes to complete + echo "♾️ Change detected: compiling and uploading..." + task sketch:compile-and-upload|| { + echo "Compile/upload failed, continuing to watch..." + continue + } + echo + done sketch:compile-and-upload: desc: "Compile and upload Arduino sketch" silent: true cmds: - | - TOTAL_START=$(date +%s) - /usr/bin/time -f "♾️ Compiled in %es" arduino-cli compile --build-path .cache -b arduino:zephyr:unoq sketch/ - /usr/bin/time -f "♾️ Uploaded in %es" arduino-cli upload -p /dev/ttyACM0 -b arduino:zephyr:unoq:flash_mode=ram --input-dir .cache sketch/ - TOTAL_END=$(date +%s) - echo "♾️ Total time: $((TOTAL_END - TOTAL_START))s" + TOTAL_START=$(date +%s) + /usr/bin/time -f "♾️ Compiled in %es" arduino-cli compile --build-path .cache -b arduino:zephyr:unoq sketch/ + /usr/bin/time -f "♾️ Uploaded in %es" arduino-cli upload -p /dev/ttyACM0 -b arduino:zephyr:unoq:flash_mode=ram --input-dir .cache sketch/ + TOTAL_END=$(date +%s) + echo "♾️ Total time: $((TOTAL_END - TOTAL_START))s" # even if not necessary: the sketch folder is sync into the board to have the updated version - adb push ./sketch/ /home/arduino/ArduinoApps/scratch-arduino-app/ @@ -168,12 +168,12 @@ tasks: desc: "Watch sketch folder for changes and auto-compile/upload" cmds: - | - echo "🐍 Python container logs" - while true; do - CONTAINER_ID=$(adb shell "docker ps -q --filter 'ancestor=ghcr.io/arduino/app-bricks/python-apps-base:0.5.0'") - if [ -n "$CONTAINER_ID" ]; then - adb shell "docker logs -f $CONTAINER_ID" - else - echo "[warning] No running container found with image ghcr.io/arduino/app-bricks/python-apps-base" - fi - done + echo "🐍 Python container logs" + while true; do + CONTAINER_ID=$(adb shell "docker ps -q --filter 'ancestor=ghcr.io/arduino/app-bricks/python-apps-base:0.5.0'") + if [ -n "$CONTAINER_ID" ]; then + adb shell "docker logs -f $CONTAINER_ID" + else + echo "[warning] No running container found with image ghcr.io/arduino/app-bricks/python-apps-base" + fi + done diff --git a/scratch-prg-extensions/extensions/src/arduino_basics/index.ts b/scratch-prg-extensions/extensions/src/arduino_basics/index.ts index 027b5d8..a5b23d8 100644 --- a/scratch-prg-extensions/extensions/src/arduino_basics/index.ts +++ b/scratch-prg-extensions/extensions/src/arduino_basics/index.ts @@ -1,100 +1,111 @@ -import { type Environment, extension, type ExtensionMenuDisplayDetails, scratch } from "$common"; +import { + type Environment, + extension, + type ExtensionMenuDisplayDetails, + scratch, +} from "$common"; import { io, Socket } from "socket.io-client"; import MatrixArgument from "./MatrixArgument.svelte"; const details: ExtensionMenuDisplayDetails = { - name: "Basic", - description: "Play with UNO Q matrix, leds and pins", - iconURL: "matrix.png", - insetIconURL: "unoq.svg", - tags: ["Arduino UNO Q"], - blockColor: "#00878F", - menuColor: "#8C7965", - menuSelectColor: "#62AEB2", + name: "Basic", + description: "Play with UNO Q matrix, leds and pins", + iconURL: "matrix.png", + insetIconURL: "unoq.svg", + tags: ["Arduino UNO Q"], + blockColor: "#00878F", + menuColor: "#8C7965", + menuSelectColor: "#62AEB2", }; // 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; + const urlParams = new URLSearchParams(window.location.search); + const boardHost = urlParams.get("host"); + if (boardHost) { + return boardHost; + } + return window.location.hostname; }; // TODO: make the block to support the brightness `0-7' of the leds const PATTERNS = { - heart: [ - [0, 0, 0, 7, 7, 0, 0, 0, 7, 7, 0, 0, 0], - [0, 0, 7, 0, 0, 7, 0, 7, 0, 0, 7, 0, 0], - [0, 7, 0, 0, 0, 0, 7, 0, 0, 0, 0, 7, 0], - [0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0], - [0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0], - [0, 0, 0, 7, 0, 0, 0, 0, 0, 7, 0, 0, 0], - [0, 0, 0, 0, 7, 0, 0, 0, 7, 0, 0, 0, 0], - [0, 0, 0, 0, 0, 7, 0, 7, 0, 0, 0, 0, 0], - ] as number[][], - arduino: [ - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - [0, 0, 7, 7, 7, 0, 0, 0, 7, 7, 7, 0, 0], - [0, 7, 0, 0, 0, 7, 0, 7, 0, 0, 0, 7, 0], - [7, 0, 0, 0, 0, 0, 7, 0, 0, 7, 0, 0, 7], - [7, 0, 7, 7, 7, 0, 7, 0, 7, 7, 7, 0, 7], - [7, 0, 0, 0, 0, 0, 7, 0, 0, 7, 0, 0, 7], - [0, 7, 0, 0, 0, 7, 0, 7, 0, 0, 0, 7, 0], - [0, 0, 7, 7, 7, 0, 0, 0, 7, 7, 7, 0, 0], - ] as number[][], - empty: Array(8).fill(null).map(() => Array(13).fill(0)) as number[][], + heart: [ + [0, 0, 0, 7, 7, 0, 0, 0, 7, 7, 0, 0, 0], + [0, 0, 7, 0, 0, 7, 0, 7, 0, 0, 7, 0, 0], + [0, 7, 0, 0, 0, 0, 7, 0, 0, 0, 0, 7, 0], + [0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0], + [0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0], + [0, 0, 0, 7, 0, 0, 0, 0, 0, 7, 0, 0, 0], + [0, 0, 0, 0, 7, 0, 0, 0, 7, 0, 0, 0, 0], + [0, 0, 0, 0, 0, 7, 0, 7, 0, 0, 0, 0, 0], + ] as number[][], + arduino: [ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 0, 7, 7, 7, 0, 0, 0, 7, 7, 7, 0, 0], + [0, 7, 0, 0, 0, 7, 0, 7, 0, 0, 0, 7, 0], + [7, 0, 0, 0, 0, 0, 7, 0, 0, 7, 0, 0, 7], + [7, 0, 7, 7, 7, 0, 7, 0, 7, 7, 7, 0, 7], + [7, 0, 0, 0, 0, 0, 7, 0, 0, 7, 0, 0, 7], + [0, 7, 0, 0, 0, 7, 0, 7, 0, 0, 0, 7, 0], + [0, 0, 7, 7, 7, 0, 0, 0, 7, 7, 7, 0, 0], + ] as number[][], + empty: Array(8) + .fill(null) + .map(() => Array(13).fill(0)) as number[][], } as const; -export default class ArduinoBasics extends extension(details, "ui", "customArguments") { - private socket: Socket | null = null; +export default class ArduinoBasics extends extension( + details, + "ui", + "customArguments", +) { + private socket: Socket | null = null; - init(env: Environment) { - const arduinoBoardHost = getArduinoBoardHost(); - var serverURL = `wss://${arduinoBoardHost}:7000`; + init(env: Environment) { + const arduinoBoardHost = getArduinoBoardHost(); + var serverURL = `wss://${arduinoBoardHost}:7000`; - console.log("Connecting to Uno Q", serverURL); + console.log("Connecting to Uno Q", serverURL); - this.socket = io(serverURL, { - path: "/socket.io", - transports: ["polling", "websocket"], - autoConnect: true, - }); + this.socket = io(serverURL, { + path: "/socket.io", + transports: ["polling", "websocket"], + autoConnect: true, + }); - this.socket.on("connect", () => { - console.log(`Connected to Arduino UNO Q`); - }); + this.socket.on("connect", () => { + console.log(`Connected to Arduino UNO Q`); + }); - this.socket.on("disconnect", (reason) => { - console.log(`Disconnected from Arduino UNO Q: ${reason}`); - }); - } + this.socket.on("disconnect", (reason) => { + console.log(`Disconnected from Arduino UNO Q: ${reason}`); + }); + } - @scratch.command(function(_, tag) { - const arg = this.makeCustomArgument({ - component: MatrixArgument, - initial: { - value: PATTERNS.arduino, - text: "arduino", - }, - }); - return tag`draw ${arg} matrix`; - }) - drawMatrix(matrix: number[][]) { - var matrixString = matrix.flat().join(""); - console.log("received matrix update", matrixString); - if (this.socket) { - this.socket.emit("matrix_draw", { frame: matrixString }); + @scratch.command(function (_, tag) { + const arg = this.makeCustomArgument({ + component: MatrixArgument, + initial: { + value: PATTERNS.arduino, + text: "arduino", + }, + }); + return tag`draw ${arg} matrix`; + }) + drawMatrix(matrix: number[][]) { + var matrixString = matrix.flat().join(""); + console.log("received matrix update", matrixString); + if (this.socket) { + this.socket.emit("matrix_draw", { frame: matrixString }); + } } - } - @scratch.command`Clear matrix` - clearMatrix(matrix: number[][]) { - var matrixString = PATTERNS.empty.flat().join(""); - if (this.socket) { - this.socket.emit("matrix_draw", { frame: matrixString }); + @(scratch.command`Clear matrix`) + clearMatrix() { + var matrixString = PATTERNS.empty.flat().join(""); + if (this.socket) { + this.socket.emit("matrix_draw", { frame: matrixString }); + } } - } } diff --git a/sketch/sketch.ino b/sketch/sketch.ino index 0dac15f..6cf1315 100644 --- a/sketch/sketch.ino +++ b/sketch/sketch.ino @@ -1,4 +1,5 @@ #include +#include "ArduinoGraphics.h" #include "Arduino_LED_Matrix.h" #include @@ -7,6 +8,9 @@ ModulinoButtons buttons; void setup() { matrix.begin(); + matrix.textFont(Font_5x7); + matrix.textScrollSpeed(100); + Bridge.begin(); Modulino.begin(Wire1); // show led indication if buttons cannot be initilized @@ -48,6 +52,10 @@ void loop() { uint8_t shades[104]; void matrix_draw(String frame){ + matrix.beginText(0, 0, 127, 0, 0); // X, Y, then R, G, B + matrix.print(" arduino.cc/uno-q "); + matrix.endText(SCROLL_LEFT); + if (frame.length() != 104) { Serial.println("Error: Frame length must be 104 characters."); return; diff --git a/sketch/sketch.yaml b/sketch/sketch.yaml index 8024762..72bd4aa 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) + - ArduinoGraphics (1.1.4) default_profile: default