跳到正文
cccskills
免费在 GitHub 公开

library-migration-guide

Follow a workflow for converting Arduino and ESP32 libraries into Aily Blockly libraries. The guide covers analyzing public APIs, defining blocks, generating code, configuring toolboxes, initializing Serial, I2C, and SPI, adapting boards, and packaging the result.

中文简介正在准备中。当前显示英语简介。

原文查看原文说明

Complete guide for converting Arduino/ESP32 hardware libraries into Aily Blockly compatible format. Covers the full workflow: source analysis, block.json design, generator.js implementation, toolbox.json configuration, bus initialization (Serial/I2C/SPI), board adaptation, and packaging.

ailyProject/aily-blockly星标 3,859(GitHub 3,859 + cccskills 0)

实测评分 未实测

查看安装方法

适用场景

  • Convert an Arduino library for Aily Blockly
  • Design block definitions and generated code
  • Adapt a library to supported development boards

包含的文件(2)

  • SKILL.md12.3 KB
  • Blockly_Library_CODE_Conventions.md27.5 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

Blockly Library Conversion Guide

A systematic guide for converting Arduino libraries into Aily Blockly libraries, based on real conversion cases (ArduinoJson, OneButton, MQTT/PubSubClient, DHT, INA219, VL53L0X, etc.).

Conversion Workflow

Prerequisites

  1. Use the existing environment/project context first to determine whether a project is already open.
  2. If no project exists, use project with action="create" to create one, then continue with the new project path.

⚠️ CRITICAL: Library Working Directory

All library files MUST be created in <projectPath>/<library-name>/, NOT in node_modules/.

  • ✅ Correct: <projectPath>/lib-grove_motor/block.json
  • ❌ Wrong: <projectPath>/node_modules/@aily-project/lib-grove_motor/block.json

The node_modules/ directory is managed by npm and is write-protected. After creating the library locally, install it with: npm install ./<library-name>

If a file write operation returns a path/permission error, check your target path — you are likely writing to node_modules/ instead of <projectPath>/<library-name>/. Fix the path and retry. Do NOT attempt to bypass by using terminal commands (mkdir, echo, Out-File, etc.) to write into protected directories.

Step-by-step Process

  1. Source Analysis: Analyze the Arduino library header files to identify public APIs. Classify by operation type: initialization, connection, communication, status, maintenance, quick operations.

  2. Block Design: Design user-friendly blocks following block type mapping rules (see Section "Block Design Rules" below).

  3. Create Library Files in <projectPath>/<library-name>/:

    • block.json — Block definitions
    • generator.js — Code generator
    • toolbox.json — Toolbox configuration
    • package.json — Library metadata
  4. Copy Source Files: Use run_terminal to copy the Arduino source:

    • If src/ folder exists → copy to <projectPath>/<library-name>/src/<library-name>/
    • If no src/ folder → copy .c, .cpp, .h, .hpp files to <projectPath>/<library-name>/src/<library-name>/
  5. Write README.md: First read Blockly_Library_README_Conventions.md (fetch from https://blockly.diandeng.tech/files/Blockly_Library_README_Conventions.md), then follow its format.

  6. Post-conversion: Ask the user if they need help with:

    • Installing: npm i <library-path> (must specify the local library path)
    • Testing the converted library
    • Opening the library folder location

Library Directory Structure

library-name/
├─ block.json        // Block definitions
├─ generator.js      // Code generator
├─ toolbox.json      // Toolbox configuration
├─ package.json      // Library metadata
├─ README.md         // Human-readable documentation
├─ README_AI.md      // LLM-readable documentation
└─ src/
   └─ library-name/  // Copied Arduino source files

Detailed Code Specification

IMPORTANT: For complete code specification with detailed examples, read the companion file Blockly_Library_CODE_Conventions.md located in this skill's folder. It covers:

  • Full block.json design rules with templates for every block type
  • Complete generator.js implementation patterns with real-world examples
  • toolbox.json shadow blocks and organization
  • package.json configuration with board compatibility
  • Board adaptation patterns and WiFi library selection

The sections below summarize the critical rules that MUST be followed.


Block Design Rules

Block Type Mapping

Arduino PatternBlock TypeConnectionField Type
Object creation/initStatementprev/nextfield_input (user enters new var name)
Global object methodStatementprev/nextNo variable field (direct call)
Object method callStatementprev/nextfield_variable (select existing var)
Global object queryValueoutputNo variable field
Quick operationStatement/ValuestandardNo variable field, direct params
Event callbackHat blockNo prev/nextfield_variable + input_statement
Conditional callbackHybridprev/nextinput_value + input_statement
Status queryValueoutputfield_variable

field_input vs field_variable

  • field_input: For initialization blocks — user enters a NEW variable name
  • field_variable: For method call blocks — user selects an EXISTING variable (set variableTypes and defaultType)
  • Global objects (Serial, WiFi, Wire, SPI, httpUpdate, SPIFFS, ESP, EEPROM): No variable field needed

Reading Variable Names in generator.js

// field_input
const varName = block.getFieldValue('VAR') || 'defaultVar';

// field_variable
const varField = block.getField('VAR');
const varName = varField ? varField.getText() : 'defaultVar';

// Global object — use directly
const serialPort = block.getFieldValue('SERIAL') || 'Serial';

Board Config Template Variables (block.json)

Use these in field_dropdown options — auto-populated at runtime:

VariableUsage
${board.i2c}I2C interface list (Wire selector)
${board.digitalPins}Digital pin list
${board.analogPins}Analog pin list
${board.serialPort}Serial port list
${board.serialSpeed}Baud rate list
${board.interruptPins}Interrupt pin list
${board.interruptMode}Interrupt mode list

Extensions

Register dynamic extensions in generator.js. Always unregister before registering:

if (Blockly.Extensions.isRegistered('ext_name')) {
  Blockly.Extensions.unregister('ext_name');
}
Blockly.Extensions.register('ext_name', function() { /* ... */ });

Code Generation Rules

Injection Methods & Execution Order

All injection methods take (tag, code) and auto-deduplicate by tag.

#include <Lib.h>        // addLibrary(tag, code)
#define MACRO val        // addMacro(tag, code)

Type globalVar;          // addVariable(tag, code)
MyClass obj;             // addObject(tag, code)
void helper() {}         // addFunction(tag, code, isGlobal?)

void setup() {
  Serial.begin(9600);    // addSetupBegin — bus-level init ONLY
  sensor.begin();        // addSetup — device/sensor init
  attachCb(handler);     // addSetupEnd — callbacks, depends on prior init
}

void loop() {
  btn.tick();            // addLoopBegin — polling/tick calls
  // [user blocks here]
}

Bus Initialization (MANDATORY)

Serial — Always use ensureSerialBegin(), NEVER write Serial.begin() directly:

// ✅ Correct
ensureSerialBegin('Serial', generator);           // default 9600
ensureSerialBegin('Serial', generator, 115200);   // custom baud
ensureSerialBegin(serialPort, generator, baud);   // dynamic

// ❌ FORBIDDEN
generator.addSetupBegin('serial_begin', 'Serial.begin(9600);');

I2C — Use wire_${wireName}_begin key for deduplication:

const wire = block.getFieldValue('WIRE') || 'Wire';
generator.addLibrary('Wire', '#include <Wire.h>');
const wireBeginKey = `wire_${wire}_begin`;
if (!generator.setupCodes_ || !generator.setupCodes_[wireBeginKey]) {
  generator.addSetup(wireBeginKey, wire + '.begin();\n');
}

SPI — Use spi_${spiName}_begin key for deduplication:

const spi = block.getFieldValue('SPI') || 'SPI';
generator.addLibrary('SPI', '#include <SPI.h>');
generator.addSetup(`spi_${spi}_begin`, spi + '.begin();\n');

Variable Management

Initialization blocks with field_input MUST implement a rename listener:

if (!block._varMonitorAttached) {
  block._varMonitorAttached = true;
  block._varLastName = block.getFieldValue('VAR') || 'defaultVar';
  registerVariableToBlockly(block._varLastName, 'VarType');
  const varField = block.getField('VAR');
  if (varField) {
    const orig = varField.onFinishEditing_;
    varField.onFinishEditing_ = function(newName) {
      if (typeof orig === 'function') orig.call(this, newName);
      const ws = block.workspace || Blockly.getMainWorkspace?.();
      const oldName = block._varLastName;
      if (ws && newName && newName !== oldName) {
        renameVariableInBlockly(block, oldName, newName, 'VarType');
        block._varLastName = newName;
      }
    };
  }
}

Generator Return Values

  • Statement blocks: return 'code;\n';
  • Value blocks: return [expr, generator.ORDER_ATOMIC];
  • Hat / event blocks: return ''; (empty string — event-driven, not in main flow)
  • Hybrid blocks: return 'conditional_code;\n'; (returned code runs inside parent callback)

valueToCode & ORDER Constants

// Extract input value (most cases use ORDER_ATOMIC)
const val = generator.valueToCode(block, 'INPUT', generator.ORDER_ATOMIC) || '0';

// Return value block
return [varName + '.read()', generator.ORDER_FUNCTION_CALL];

Board Adaptation

Access runtime board config via window['boardConfig']:

const boardConfig = window['boardConfig'];
// boardConfig.core — e.g. 'esp32:esp32', 'arduino:avr'
// boardConfig.name — board display name
// boardConfig.i2c — I2C interface list
// boardConfig.digitalPins — digital pin list

NEVER modify window['boardConfig'] directly — use independent storage like window['customXxx'].


toolbox.json Rules

All input_value slots MUST have shadow blocks:

{
  "kind": "block",
  "type": "sensor_read",
  "inputs": {
    "TIMEOUT": {"shadow": {"type": "math_number", "fields": {"NUM": 1000}}}
  }
}

Organize blocks by user cognitive flow using label separators:

{
  "kind": "category",
  "name": "SensorLib",
  "contents": [
    {"kind": "label", "text": "Setup"},
    {"kind": "block", "type": "sensor_init"},
    {"kind": "label", "text": "Read Data"},
    {"kind": "block", "type": "sensor_read"}
  ]
}

package.json Configuration

{
  "name": "@aily-project/lib-libname",
  "nickname": "Display Name",
  "description": "Brief description (<50 chars)",
  "version": "1.0.0",
  "compatibility": {
    "core": [],
    "voltage": [3.3, 5]
  },
  "keywords": ["aily", "blockly"],
  "tested": true,
  "url": "original library URL"
}

Board compatibility shorthand:

  • Universal: "core": [] (empty array = all boards)
  • ESP32 only: "core": ["esp32:esp32"]
  • Classic Arduino: "core": ["arduino:avr", "arduino:megaavr"]
  • IoT boards: "core": ["esp32:esp32", "esp8266:esp8266", "renesas_uno:unor4wifi"]

Common Anti-patterns

❌ Wrong✅ Correct
Write Serial.begin() directlyUse ensureSerialBegin(port, generator)
Hardcoded Wire key 'WIRE_BEGIN'Use wire_${wireName}_begin formatted key
Modify window['boardConfig']Use window['customXxx'] for custom storage
addSetupBegin for sensor initaddSetupBegin is bus-only; use addSetup for sensors
No shadow blocks for input_valueAlways configure shadow blocks in toolbox.json
Extension register without unregisterAlways unregister() before register()
addFunction without 3rd paramPass true when helper function must be globally visible
Skip ensureSerialBegin in quick opsQuick operations using Serial.println need it too

Quality Checklist

  • Covers 80%+ core features of the original library
  • New users can get started within 10 minutes
  • Common tasks complete in ≤ 3 steps
  • Generated code compiles 100%
  • Supports target development boards
  • All input_value have shadow blocks in toolbox.json
  • Serial init uses ensureSerialBegin()
  • I2C init uses wire_${wire}_begin key deduplication
  • SPI init uses spi_${spi}_begin key deduplication
  • addSetupBegin only used for bus-level initialization
  • Extensions unregister before register
  • Never directly modifies window['boardConfig']
  • Variable rename listener implemented on field_input blocks

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

Use this reference to write Aily Block Syntax (ABS), a text format for Blockly scripts. It explains block connections, parameter order, statement inputs, values, and variable references, with examples for conditions, loops, and Arduino program structure.

中文简介正在准备中。当前显示英语简介。

  • Check the correct order of block parameters
  • Write conditions and loops in ABS
  • Look up value and variable syntax for Blockly scripts
ailyProject/aily-blockly38592026年10月26日 更新

Implement or modify Blockly/ABS programs using the project’s current board and installed libraries. Check only relevant library evidence, edit the canonical ABS source, sync it to the workspace, and run focused validation when needed.

中文简介正在准备中。当前显示英语简介。

  • Adding a feature to an existing Blockly program
  • Editing ABS source and syncing the workspace
  • Checking a focused build or syntax issue
ailyProject/aily-blockly38592026年10月26日 更新

Plan a new Blockly hardware project by discovering suitable board and library packages, then comparing viable combinations. Each plan outlines wiring assumptions, workspace structure, implementation steps, and validation before the user confirms project creation.

中文简介正在准备中。当前显示英语简介。

  • Comparing boards for a new Blockly project
  • Planning libraries for a sensor or actuator
  • Reviewing wiring and validation assumptions
ailyProject/aily-blockly38592026年10月26日 更新

chronicle

免费

Search indexed chat history to find earlier decisions, tool activity, project summaries, files, and workflow tips. It can also rebuild the local history index when requested or when search results appear out of date.

中文简介正在准备中。当前显示英语简介。

  • Find a decision made in an earlier conversation
  • Check which tools were used for past work
  • Summarize recent project sessions
ailyProject/aily-blockly38592026年10月26日 更新

handoff

免费

Create a concise handoff document in the operating system’s temporary directory so another agent can continue the work. It summarizes the conversation, suggests relevant skills, references existing artifacts, and redacts sensitive information.

中文简介正在准备中。当前显示英语简介。

  • Hand ongoing work to another agent
  • Prepare context for a fresh session
  • Summarize progress without duplicating project artifacts
ailyProject/aily-blockly38592026年10月26日 更新

improve

免费

Survey a codebase and turn verified improvement opportunities into prioritized, self-contained implementation plans for another agent. It can assess bugs, security, performance, tests, technical debt, migrations, tooling, and product direction while keeping source code read-only.

中文简介正在准备中。当前显示英语简介。

  • Audit a codebase for high impact issues
  • Plan fixes for security or performance findings
  • Create a grounded roadmap for another agent
ailyProject/aily-blockly38592026年10月26日 更新

查看 ailyProject 的全部技能

报告此技能的问题