本文へ移動
cccskills
無料GitHub で公開日本語紹介

library-migration-guide

ArduinoやESP32のライブラリをAily Blockly向けに変換するため、公開APIの分析からブロック定義、コード生成、ボード対応、パッケージ構成までの手順を確認できます。

原文原文の説明を見る

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)

実行スコア 未検証

インストール方法を見る

こんなときに便利

  • ArduinoライブラリをBlockly用に変換したいとき
  • ハードウェアAPIをブロック化したいとき
  • ボード別の設定と生成コードを整えたいとき

日本語での紹介

できること

ArduinoやESP32向けのハードウェアライブラリを、Aily Blocklyで使えるライブラリ形式に変換する手順を確認できます。ヘッダーファイルから公開APIを分類し、block.jsonでブロックを設計し、generator.jsでコード生成を実装、toolbox.jsonやpackage.jsonを整える流れを扱います。Serial、I2C、SPIの初期化、変数の扱い、ボード設定への適応、元ライブラリのソース配置、README作成についても具体例があります。完成後のインストールやテストを案内する手順も含まれています。

こんなときに便利

既存のArduinoライブラリをビジュアルブロックから操作したいときや、センサー、通信機器などのライブラリをAily Blocklyに移植したいときに向いています。ブロック定義や生成コードの設計方針を一通り確認できます。

使い方の例

  • 「このArduinoライブラリをAily Blockly向けに変換する手順を整理してください」
  • 「公開APIから初期化ブロックと読み取りブロックを設計してください」
  • 「I2Cを使うライブラリのコード生成とボード対応を確認してください」

注意点

ライブラリファイルはプロジェクト内の<library-name>/に作成し、node_modules/には作成しません。README作成時には指定された規約ファイルを参照します。

この紹介文は、公開されている SKILL.md をもとに AI(OpenAI Codex)が作成しました。正確な仕様は下の原文を確認してください。

含まれるファイル(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 / 1000 文字

このフォームは reCAPTCHA で保護されており、Google のプライバシーポリシーと利用規約が適用されます。

同じリポジトリのスキル

abs-syntax-reference

無料概要あり

Aily BlocklyのABSでブロックの接続形式や引数順、値・変数の書き方を確認し、条件分岐やループを含むブロックコードを記述するための構文リファレンスです。

  • ABSの引数順を確認したいとき
  • 条件分岐やループを記述したいとき
  • Blocklyコードの構文を調べたいとき
ailyProject/aily-blockly3,8602026年10月26日 更新

blockly-best-practices

無料概要あり

BlocklyやABSプログラムの編集で、必要なライブラリ情報を確認し、ワークスペースへの反映と変更に応じた検証を進めます。

  • 既存のBlocklyプログラムを変更したいとき
  • ABS編集を画面に反映したいとき
  • 必要な範囲でビルドを確認したいとき
ailyProject/aily-blockly3,8602026年10月26日 更新

blockly-project-planning

無料概要あり

新しいBlocklyプロジェクトの候補ボードやライブラリを調べ、配線や検証手順を含む計画を提示して選択を促します。

  • Blocklyで新規プロジェクトを始めたいとき
  • ボードやライブラリを比較したいとき
  • センサーの構成を計画したいとき
ailyProject/aily-blockly3,8602026年10月26日 更新

chronicle

無料概要あり

過去のチャット履歴を検索し、以前の決定やツール実行、作業内容、利用上の工夫を確認できます。必要に応じて履歴の検索用インデックスも再構築します。

  • 以前の会話で決めたことを探したいとき
  • 過去に使ったツールやファイルを調べたいとき
  • 最近のプロジェクト履歴をまとめたいとき
ailyProject/aily-blockly3,8602026年10月26日 更新

handoff

無料概要あり

会話の要点や未完了の作業、参照すべき資料を一時ディレクトリの引き継ぎ文書にまとめ、次のエージェントが続きから作業できるようにします。

  • 別のエージェントに作業を引き継ぎたいとき
  • 新しいセッション用に経緯を整理したいとき
  • 既存資料を重複させず進捗をまとめたいとき
ailyProject/aily-blockly3,8602026年10月26日 更新

improve

無料概要あり

コードベースを読み取り専用で調査し、バグやセキュリティ、性能、テスト、技術的負債などの改善点を優先順位付きで整理して、別の担当者が実行できる計画にまとめます。

  • コードベースの課題を監査したいとき
  • 修正作業を別エージェントに渡したいとき
  • ロードマップ候補を根拠付きで検討したいとき
ailyProject/aily-blockly3,8602026年10月26日 更新

ailyProject のスキルをすべて見る

このスキルの問題を報告する