본문으로 건너뛰기
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,860(GitHub 3,860 + 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(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

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 / 1,000자

이 양식은 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-blockly3.9천2026년 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-blockly3.9천2026년 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-blockly3.9천2026년 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-blockly3.9천2026년 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-blockly3.9천2026년 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-blockly3.9천2026년 10월 26일 업데이트

ailyProject의 스킬 모두 보기

이 스킬의 문제 신고