# Easyeda Draw Symbol

> Create custom schematic symbols for EasyEDA Pro. Invoke when creating custom symbols, building devices from symbols, or when system library lacks required components.

- Skill: `kukucaicndy/easyeda-draw-symbol` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds@latest add kukucaicndy/easyeda-draw-symbol`
- Raw SKILL.md: https://api.skillmd.com/api/skills/kukucaicndy/easyeda-draw-symbol/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: kukucaicndy (https://skillmd.com/u/kukucaicndy)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/kukucaicndy/easyeda-draw-symbol

---


# 自定义符号创建

系统库中找不到所需器件符号时（如开发板模块、定制 IC），通过此流程创建自定义符号。

---

## 1. 什么时候需要自定义符号

- 系统库搜索不到（如 `nice!nano v2`、`Pro Micro` 等开发板模块）
- 有现成符号但引脚功能与设计不匹配
- 需要自定义引脚排列以优化原理图可读性

## 2. 创建流程

```
创建空符号 → 添加引脚/形状/文本 → 创建器件 → 在原理图中使用
```

### Step 1: 创建空符号

```javascript
const personalLib = await eda.lib_LibrariesList.getPersonalLibraryUuid();
const symbolUuid = await eda.lib_Symbol.create(personalLib, "nice!nano v2");
// symbolUuid = "57ad2c820a1646eb8a6cfa1ff35c8922"
```

### Step 2: 在符号编辑器中添加引脚和形状

打开符号编辑器，然后使用 `SCH_Primitive*` API 添加图元：

```javascript
// 打开符号编辑器
await eda.lib_Symbol.openInEditor(symbolUuid, personalLib);

// 创建矩形主体
// ⚠️ 符号编辑器 Y 轴向上增长（数学坐标系），topLeftY 是矩形最高点
//    矩形从 topLeftY 向下延伸 height，所以 topLeftY=130 → 覆盖 y=-130~130
await eda.sch_PrimitiveRectangle.create(-80, 130, 160, 260, 0, 0, null, null, null, null, null);

// 创建引脚
// SCH_PrimitivePin.create(x, y, pinNumber, pinName?, rotation?, pinLength?, pinColor?, pinShape?, pinType?)
// ⚠️ 重要：(x, y) 是引脚连接点（圆点）的位置，不是引脚端点
//    连接点应该放在矩形边缘上，引脚从连接点向外延伸
// ⚠️ 重要：rotation 参数会被 API 反转存储
//    传 0 → 存储 180 → 引脚向左延伸（适合左侧引脚）
//    传 180 → 存储 0 → 引脚向右延伸（适合右侧引脚）
//    左侧引脚: x = 矩形左边缘, rotation=0 (传0→存180→向左延伸)
//    右侧引脚: x = 矩形右边缘, rotation=180 (传180→存0→向右延伸)
// ⚠️ 重要：符号编辑器 Y 轴向上增长。y=-110 在矩形中间偏上位置（屏幕上方），
//    与原理图编辑器的直觉相反（原理图 Y 轴向下增长）。
await eda.sch_PrimitivePin.create(-80, -110, "1", "D0", 0, 20, null, "None", "BI");
await eda.sch_PrimitivePin.create(80, -110, "13", "D10", 180, 20, null, "None", "BI");

// 创建方向标识圆点（指示引脚 1 位置，在矩形左上角内部）
// 圆点坐标 (-70, 120)：X 比左边缘 (-80) 略靠内，Y 在矩形顶部内侧
await eda.sch_PrimitiveCircle.create(-70, 120, 5, null, null, null, null, null);
```

### Step 3: 创建器件（绑定符号）

```javascript
const deviceUuid = await eda.lib_Device.create(personalLib, "nice!nano v2", undefined, {
  symbolType: "sch",
  symbol: { uuid: symbolUuid, libraryUuid: personalLib },
});
```

### Step 4: 在原理图中使用

```javascript
// 搜索自定义器件（需指定个人库 UUID）
const results = await eda.lib_Device.search("nice!nano v2", personalLib);
// 放置器件
await eda.sch_PrimitiveComponent.create(
  { libraryUuid: personalLib, uuid: results[0].uuid },
  x, y, "", 0, false, true, true
);
```

---

## 3. 引脚创建 API

### 参数说明

```typescript
SCH_PrimitivePin.create(
  x: number,                              // 引脚连接点 X 坐标（圆点位置，放在矩形边缘）
  y: number,                              // 引脚连接点 Y 坐标
  pinNumber: string,                      // 引脚编号（如 "1", "2"）
  pinName?: string,                       // 引脚名称（如 "D0", "VCC"）
  rotation?: number,                      // 旋转角度: 0, 90, 180, 270（⚠️ 传入值与存储值相反，见下方说明）
  pinLength?: number,                     // 引脚长度（从连接点向外延伸）
  pinColor?: string | null,               // 引脚颜色，null=默认
  pinShape?: ESCH_PrimitivePinShape,      // 引脚形状，"None"|"Clock"|"Inverted"|"Inverted Clock"
  pinType?: ESCH_PrimitivePinType         // 引脚类型
)
```

> ⚠️ **pinName 为空时的行为**：`pinName` 传 `undefined` 或 `null` 时，引脚只显示编号不显示名称。传空字符串 `""` 效果相同。如果符号中部分引脚不需要名称（如仅用于内部连接的 GND 引脚），可省略 `pinName` 参数。

> ⚠️ **重要：`(x, y)` 是引脚连接点（圆点）的位置，不是引脚端点。**
> 引脚从连接点沿 `rotation` 方向延伸 `pinLength` 单位。
> 连接点应该放在矩形边缘上，这样引脚才能正确对齐。
>
> 错误示例：`x=-100, rotation=0, 矩形边缘=-80` → 连接点和矩形之间有 20 单位间隙 ❌
> 正确示例：`x=-80, rotation=0, 矩形边缘=-80` → 连接点正好在矩形边缘 ✅

### 引脚类型 (pinType)

| 类型 | 说明 | 适用场景 |
|------|------|---------|
| `"BI"` | 双向 | GPIO 引脚 |
| `"IN"` | 输入 | RESET、中断输入 |
| `"OUT"` | 输出 | TX、PWM 输出 |
| `"Power"` | 电源 | VCC、VDD、RAW |
| `"Ground"` | 地 | GND、VSS |
| `"Passive"` | 无源 | 模拟引脚 |
| `"Undefined"` | 未定义 | 不使用的引脚 |

### 引脚方向 (rotation)

> ⚠️ **API 反转行为：`sch_PrimitivePin.create()` 传入的 rotation 值会被反转为存储值。**
> 传 0 → 存储 180（向左）→ 引脚向左延伸
> 传 180 → 存储 0（向右）→ 引脚向右延伸
> 传 90 → 存储 270（向下）→ 引脚向下延伸
> 传 270 → 存储 90（向上）→ 引脚向上延伸

| 传入 rotation | 存储 rotation | 引脚方向 | 适用侧 |
|----------|---------|--------|--------|
| `0` | 180 | 向左 | 符号左侧引脚 |
| `90` | 270 | 向下 | 符号底部引脚 |
| `180` | 0 | 向右 | 符号右侧引脚 |
| `270` | 90 | 向上 | 符号顶部引脚 |

---

## 4. 矩形创建 API

```typescript
SCH_PrimitiveRectangle.create(
  topLeftX: number,       // 左上角 X
  topLeftY: number,       // 左上角 Y（⚠️ Y 轴向上增长，topLeftY 是矩形最高点，向下延伸 height）
  width: number,          // 宽度
  height: number,         // 高度（从 topLeftY 向下延伸）
  cornerRadius?: number,  // 圆角半径
  rotation?: number,      // 旋转角度
  color?: string | null,  // 边框颜色
  fillColor?: string | null, // 填充颜色
  lineWidth?: number | null, // 线宽 1-10
  lineType?: ESCH_PrimitiveLineType | null,
  fillStyle?: ESCH_PrimitiveFillStyle | null
)
```

> ⚠️ **符号编辑器 Y 轴方向：与屏幕坐标不同，符号编辑器 Y 轴向上增长（数学坐标系）。**
> `topLeftY` 是矩形的最高点（Y 值最大的边），矩形从此点向下延伸 `height`。
> 例如：要让矩形覆盖 y=-130 到 y=130，应传 `topLeftY=130, height=260`（不是 topLeftY=-130）。

---

## 5. CLI 命令

```bash
# 创建空符号到个人库
./scripts/draw_cli.py create-symbol "器件名称"

# 在编辑器中打开符号
./scripts/draw_cli.py open-symbol <symbolUuid> <libUuid>

# 从符号创建器件
./scripts/draw_cli.py create-device <名称> <symUuid> <libUuid>

# 一键构建符号（创建+打开+等待编辑）
./scripts/draw_cli.py build-symbol "器件名称"

# 搜索器件（默认系统库，指定个人库 UUID）
./scripts/draw_cli.py search "nice!nano v2" 5 --lib <libraryUuid>
```

---

## 6. 参考示例

### 6.1 符号编辑器与原理图编辑器 API 差异

| 操作 | 原理图编辑器 | 符号编辑器 |
|------|-------------|-----------|
| 查询引脚 | `sch_PrimitivePin.getAll()` ✅ | `sch_PrimitivePin.getAll()` ✅ |
| 查询矩形 | `sch_PrimitiveRectangle.getAll()` ✅ | ❌ **返回空数组** |
| 查询圆 | `sch_PrimitiveCircle.getAll()` ✅ | ❌ **返回空数组** |
| 查询引脚 ID | `sch_PrimitivePin.getAllPrimitiveId()` ✅ | `sch_PrimitivePin.getAllPrimitiveId()` ✅ |
| 查询矩形 ID | `sch_PrimitiveRectangle.getAllPrimitiveId()` ✅ | ❌ **返回空数组** |
| 查询圆 ID | `sch_PrimitiveCircle.getAllPrimitiveId()` ✅ | ❌ **返回空数组** |
| 查询所有图元 | 各类型 `getAll()` 方法 | `sch_SelectControl.getAllSelectedPrimitives()` ✅（需先选中） |
| 获取图元类型 | `sch_Primitive.getPrimitiveTypeByPrimitiveId(id)` ✅ | ✅ 可用 |
| 获取图元属性 | `sch_Primitive.getPrimitiveByPrimitiveId(id)` ✅ | ✅ 可用 |

**结论：在符号编辑器中，不要依赖 `sch_PrimitiveRectangle.getAll()` 和 `sch_PrimitiveCircle.getAll()` 等类型专属批量查询方法。** 应使用 `sch_PrimitivePin.getAllPrimitiveId()` 获取所有引脚 ID，再通过 `sch_Primitive.getPrimitiveByPrimitiveId(id)` 逐个查询。或使用 `sch_SelectControl.getAllSelectedPrimitives()` 获取已选中图元（圆、矩形等均可用此方式查询）。

### 6.2 nice!nano v2 符号

符号参数：
- 24 个引脚（左 12 + 右 12）
- 引脚间距 20 单位
- 主体矩形 160×260（topLeftX=-80, topLeftY=130）
- 引脚长度 20
- 方向标识：圆点在矩形左上角内部 (-70, 120)，半径 5，指示引脚 1 位置
- **引脚顺序（DIP 标准）**：
  - 左列 Pin 1-12：从上到下，y = 110, 90, 70, 50, 30, 10, -10, -30, -50, -70, -90, -110
  - 右列 Pin 13-24：从下到上，y = -110, -90, -70, -50, -30, -10, 10, 30, 50, 70, 90, 110

### 6.3 引脚布局规范

**引脚名称在器件主体内部（推荐）：**

```
     ───┐ D0              GND ├───
     ───┤ D1              VCC ├───
     ───┤ D2              GND ├───
     ───┤ D3   ┌────────┐ RAW ├───
     ───┤ D4   │ 器件主体 │ VCC ├───
     ───┤ D5   │ 160×260 │ GND ├───
     ───┤ D6   └────────┘ RESET ├──
     ───┤ D7   连接点     D21 ├───
     ───┤ D8   x=-80/80   D20 ├───
     ───┤ D9              D19 ├───
     ───┤ D14             D18 ├───
     ───┘ D15             D16 └───

  引脚连接点 (x,y) 在矩形边缘，引脚线在外侧
  名称文本在矩形内部，编号在引脚线上方
  左侧 (rotation=0): 名称在引脚线右侧（矩形内），偏移 +30
  右侧 (rotation=180): 名称在引脚线左侧（矩形内），偏移 -30
```

---

## 7. 引脚名称位置调整（重要发现）

> **⚠️ 关键发现：引脚名称是 ATTR 图元（不是独立的 Text 图元），通过 `parentPrimitiveId` 精确关联到 PIN！**
>
> 通过 `sch_PrimitiveAttribute.getAllPrimitiveId()` 可以获取所有 ATTR，包括 "Pin Name" 和 "Pin Number"。

### 7.1 问题现象

`sch_PrimitivePin.create()` 创建引脚时，EasyEDA 会自动生成 ATTR 图元作为名称和编号，但初始位置通常不正确：

```
PIN2 (正确):  引脚连接点(-80, 90), 名称ATTR(-50, 90) → 名称在矩形内部 ✅
PIN1 (异常):  引脚连接点(-80, 110), 名称ATTR(-103, 110) → 名称在矩形外部 ❌
```

### 7.2 原因

- 修改 `rotation` 只会旋转引脚线，**不会同步调整已生成的 ATTR 图元位置**
- ATTR 的 `alignMode`（对齐方式）和 `x`, `y` 坐标需要手动修正
- **同名引脚（如多个 GND）不能靠 `value + y` 匹配，必须用 `parentPrimitiveId` 精确匹配**

### 7.3 解决方案

使用 `set-pin-labels-position` CLI 命令批量修正引脚名称位置：

```bash
# 修正左侧引脚名称位置（名称在引脚线右侧，矩形内，左对齐）
./scripts/draw_cli.py set-pin-labels-position --side left --offset 30

# 修正右侧引脚名称位置（名称在引脚线左侧，矩形内，右对齐）
./scripts/draw_cli.py set-pin-labels-position --side right --offset 30
```

**实现原理（已固化到 `fixPinLabels`）：**
1. 通过 `sch_PrimitivePin.getAllPrimitiveId()` 获取所有引脚
2. 通过 `sch_PrimitiveAttribute.getAllPrimitiveId()` 获取所有 ATTR 图元
3. **用 `parentPrimitiveId` 精确匹配 PIN 和 ATTR**（避免同名引脚错乱）
4. 设置正确的 `alignMode` 和 `x`, `y` 坐标

或手动用 `sch_PrimitiveAttribute` API 调整：

```javascript
// 1. 获取所有 ATTR
const attrIds = await eda.sch_PrimitiveAttribute.getAllPrimitiveId();

// 2. 找到 Pin Name ATTR（必须用 parentPrimitiveId 匹配，不能只用 value）
const attr = await eda.sch_PrimitiveAttribute.get([attrId]);

// 3. 修改位置和对齐方式
const at = attr[0].toAsync();
at.setState_AlignMode(2);  // 左侧=2(左中)，右侧=8(右中)
at.setState_X(-50);
at.setState_Y(-pin.y);     // ⚠️ Y 坐标 = -pin.y（符号编辑器坐标系）
await at.done();
```

### 7.4 名称位置规律（已验证）

| 引脚侧 | rotation | alignMode 设置值 | alignMode 存储值 | 含义 | X 坐标 | Y 坐标 |
|--------|----------|-----------------|-----------------|------|--------|--------|
| 左侧 | 0 | 2 (左中) | 3 (左中) | 文字从锚点向右延伸，左对齐 | `pin.x + offset` | `-pin.y` |
| 右侧 | 180 | 8 (右中) | 5 (右中) | 文字从锚点向左延伸，右对齐 | `pin.x - offset` | `-pin.y` |
| 顶部 | 270 | 5 (正中) | 4 (正中) | 文字水平居中，在矩形内部 | `pin.x` | `-pin.y + offset` |
| 底部 | 90 | 5 (正中) | 4 (正中) | 文字水平居中，在矩形内部 | `pin.x` | `-pin.y - offset` |

**⚠️ 关键规则（实测验证）：**

1. **Y 坐标 = `-pin.y`**：`setState_Y(-pin.y)` 后 EasyEDA 内部自动翻转为 `pin.y` 存储，显示位置正确
2. **alignMode 设置值与存储值不同**：EasyEDA 内部使用列优先九宫格存储
   - 设置值（`setState_AlignMode`）：1=左上 2=左中 3=左下 4=中上 5=正中 6=中下 7=右上 8=右中 9=右下
   - 存储值（`alignMode` 属性 / `getState_AlignMode()`）：0=左上 1=中上 2=右上 3=左中 4=正中 5=右中 6=左下 7=中下 8=右下
3. **alignMode 必须用"中"锚点**（设置值 2/8/5），不要用"上/下"锚点，否则 Y 位置会偏移
4. **X 坐标是文字锚点位置**，不是文字中心：左中(2)时 X 是文字左边缘，右中(8)时 X 是文字右边缘，正中(5)时 X 是文字中心
5. **offset 推荐值 5~10**：名称与矩形边缘的距离，默认 5

**ATTR 属性说明：**
- `key`: "Pin Name" 或 "Pin Number"
- `value`: 引脚名称或编号
- `x`, `y`: ATTR 锚点位置（设置 `-pin.y` 或 `-pin.y ± offset`，存储为 `pin.y` 或 `pin.y ∓ offset`）
- `alignMode`: 对齐方式存储值（左侧=3 左中，右侧=5 右中，上下=4 正中）
- `parentPrimitiveId`: 精确关联的 PIN ID（匹配时必须使用）

---

## 7. 常见错误

| 错误 | 原因 | 解决 |
|------|------|------|
| "无法创建引脚图元" | `pinShape` 传了 `null` | 改为传 `"None"` |
| 搜索不到自定义器件 | 搜索的是系统库 | 传入个人库 UUID |
| 引脚不显示 | 符号编辑器未激活 | 先 `openInEditor` 再操作 |
| 引脚与矩形未对齐 | `(x,y)` 位置错误，放在矩形边缘 + 长度位置 | `(x,y)` 应放在矩形边缘上，不是边缘 + 长度 |
| 矩形查询不到 | `sch_PrimitiveRectangle.getAll()` 在符号编辑器中无效 | 改用 `sch_SelectControl.getAllSelectedPrimitives()` |
