跳转至

Mind+ V2 扩展库开发文档

Mind+ 以丰富的积木扩展库为特色,目前已收录 500 多个扩展库。在 V2 版本中,进一步增强了扩展库开放能力,将各积木模式的扩展库统一为一套模板;同时,软件本体与扩展库实现解耦,扩展库更新与软件本体更新分离,更新更加及时,并且可以单独切换扩展库版本。

提示

扩展库开发需有基本的代码基础并熟练使用Git版本管理工具,建议优先采用软件内置的扩展库。

Pasted image 20260613172753.png

开发流程

新扩展库开发流程

下载扩展库模板文件 > 修改配置文件及资源包 > 编写积木定义文件 > 编写生成代码文件 > 编译 > 本地测试 > 测试完成 > 推送到云端 Git 仓库 > 收录上线

扩展库版本更新流程

拉取云端最新版本 > 修改配置文件中的版本号 > 修改更新内容 > 编译 > 本地测试 > 测试完成 > 推送到云端 Git 仓库 > 收录上线

环境准备

开发 Mind+ V2 扩展库需要安装 Node.js 运行环境;由于 Mind+ V2 中各扩展库可以单独切换版本,因此版本管理要求更加严格,需要使用 Git 工具进行版本控制。

此处以 Windows 平台为例,Mind+ 升级到 V2.0.7 或以上版本。

Node.js 安装

Node.js 是一个让 JavaScript 代码能够直接在电脑上运行的环境。在 Mind+ 扩展开发中,所有构建和依赖管理工具都依赖它,因此必须先安装,就像盖房子要先打好地基一样。

  • 打开 Node.js 官网下载页面:https://nodejs.org/zh-cn/download
  • 选择 v16.20.2 版本,下载安装包,得到 .msi 文件。

    注 1:Mind+ 内置 Node.js 版本为 v16,因此使用 v16 可以获得更好的兼容性。
    注 2:如果电脑上其他应用需要更高版本,推荐使用nvm工具管理和切换版本。

Pasted image 20260516155348.png

  • 下载完成后双击 .msi 文件安装。不要修改其他选项或安装路径,直接一路单击 Next 安装即可,出现“Node.js has been successfully installed.”即表示安装完成。 Pasted image 20260516160750.png
    Pasted image 20260516160817.png

  • 按 Win+R,输入 cmd 打开终端;输入 node -v,回车后能看到 Node.js 版本为 v16;输入 npm -v,回车后能看到 npm 版本,说明 Node.js 环境安装完成。 Pasted image 20260516164058.png

注:为了后续更快地通过 Node.js 的 npm 安装依赖,此时可以将 npm 镜像源设置为国内的阿里源。 将以下两条命令粘贴到终端运行即可:

# 设置npm源
npm config set registry https://registry.npmmirror.com

# 验证是否设置成功
npm config get registry

Pasted image 20260516164206.png

Git 安装

Git 是目前最流行的版本控制系统,能像“时光机”一样记录代码的每一次修改。对于 Mind+ 这种需要精确管理扩展库不同版本的开发工作,Git 是确保版本清晰、协作有序的关键工具。

VS Code安装

VS Code 是一款免费、强大且广受欢迎的代码编辑器,它集成终端和 Git 操作等丰富功能,能极大提升编写和调试代码的效率,同时也便于使用 Git 进行可视化管理。

  • 打开 VS Code 官网,点击 Download,等待自动开始下载:https://code.visualstudio.com/
    Pasted image 20260516164335.png

  • 如果没有自动开始下载,可点击此链接重新下载。
    Pasted image 20260516164408.png

  • 双击上一步下载的安装包,除了以下两个界面按图勾选,其他页面不要修改选项,也不要修改安装路径,直接点下一步到完成即可. Pasted image 20260516164749.png

  • 配置中文:打开 VS Code 后,在左侧点击扩展图标,搜索“简体中文”,点击“Install”安装。
    Pasted image 20260516165908.png

  • 安装完成后右下角弹出是否要切换语言的对话框,点击”Change Language and Restart“ (切换语言并重启),等待软件重启后,界面就为中文了。

    注:在 VS Code 中无需登录;右侧的聊天窗口需要登录账号才能使用,本教程不使用该工具,可以将其关闭。

Pasted image 20260516170110.png

开发一个扩展库——以上传模式为例

下载模板仓库

打开Mind+ 官方扩展库开发模板仓库:https://gitee.com/mind-plus/mindplus-ext2-builder > 点击 克隆/下载 > 下载ZIP ,解压后即可得到如下文件: Pasted image 20260516171228.png

  • 文件/文件夹说明:
    • extension/:扩展库源码文件夹,当前开发的扩展库相关文件都应放到这个文件夹中。
    • examples/:其中存放各模式的扩展库示例。
    • scripts/、utils/、package.json、package-lock.json、webpack.config.js、.babelrc:这些都是扩展库的配置文件,无需操作或修改。
    • .gitignore:用于配置 Git 文件过滤规则,无需修改。
    • README.md:当前仓库的说明文件;后续将当前仓库推送到 Git 云平台时,该文件会作为网页说明内容,上线前需要将当前仓库的说明写在里面。

选择模板文件

此处我们以创建一个 上传模式 的扩展库为例,从 examples 中找到 example-upload-module 文件夹,将其中的内容 全部复制extension 文件夹中。
Pasted image 20260516172637.png

  • 注意:extension 文件夹中应直接看到 index.js。
    Pasted image 20260516172829.png

  • 如果没有文件后缀,可以打开 文件扩展名 可以看到文件后缀是.js
    Pasted image 20260516172939.png

  • 使用 VS Code 打开文件夹,如果弹出 是否信任此文件夹 ,选择“是”即可。
    Pasted image 20260516173108.png

初始化

新下载的扩展库仓库不包含相关环境,因此需要先初始化一次。

  • 点击顶部菜单>终端>新建终端,在下面打开的终端中输入 npm install ,回车后将会自动通过网络拉取需要的相关环境文件。
  • 出现 added xx packages 即表示安装完成。此时观察文件夹,会发现多了一个 node_modules 文件夹,这是初始化所需的环境文件。

    注:出现黄色 WARN 警告可忽略;如果出现红色 Error,才表示发生错误。

Pasted image 20260516175531.png

编译

  • 在终端输入 npm run build 并回车,即可触发编译;程序会自动将 extension 文件夹中的扩展源码编译成 Mind+ 可导入的扩展库。
  • 如果输出均为绿色提示,并且 build 文件夹中生成了编译后的扩展库,即表示编译成功。
    Pasted image 20260516180703.png
    Pasted image 20260809064736.png

导入 Mind+

  • 启动 Mind+ V2,打开 上传模式 ,点击界面右上角的 设置 按钮。
    Pasted image 20260516181135.png

  • 此时打开“扩展”页面,可以看到左下角新增了一个 加载测试扩展 的入口,点击后弹出文件选择框,选择 编译完成的build文件夹 中扩展库的 config.json 文件,即可导入本次测试扩展库。

    导入的时候会检测扩展库,如果报错则根据报错提示进行修改后重新编译导入。

Pasted image 20260516181259.png

  • 导入成功后,扩展左上角会显示 测试 标识。选择主板并加载此扩展后,主界面会显示该测试扩展的积木。
    Pasted image 20260516181951.png

至此,你已经了解了扩展库的文件目录、编译、导入的流程,接下来,我们来学习如何调整扩展库的源码符合自己的需求。

修改扩展库

扩展库源码文件结构

  • 扩展库源码文件夹 extension 中包含如下文件夹和文件: Pasted image 20260516182811.png

  • 完整文件结构和功能说明:

    extension/                 
    ├── icon/                  
    │   ├── blockIcon.svg      # 积木块上的图标
    │   └── menuIcon.svg       # 积木扩展加载后左侧菜单栏图标
    ├── locales/               # 多语言翻译文件目录
    │   ├── index.js           # 语言包入口文件,编译时自动生成
    │   ├── zh-cn.json         # 简体中文翻译
    │   ├── en.json            # 英文翻译
    │   └── ...                # 其他语言按语言文件名翻译
    ├── public/                
    │   ├── config.json        # 扩展库配置文件
    │   └── cover.png          # 扩展卡片上显示的图片  
    │   ├── libraries/              # Arduino 库文件目录 (可选)
    │       └── DFRobot_xxxx        # Arduino 库文件
    │         ├── xxx.cpp            # 库代码
    │         ├── xxx.h              # 库代码
    │         └── library.properties           # 库配置文件
    ├── func.js                # 定义积木拖到工作区后生成的代码
    └── index.js               # 定义积木的外观样式
    

扩展库信息修改

扩展库加载时显示的卡片信息可以定制,主要由 public 文件夹中的 config.json 和 cover.png 决定。
Pasted image 20260516183927.png

config.json 说明:

{
  "id": "DFR0067",
  "author": "DFRobotTest",
  "version": "0.0.3",
  "name": {
    "zh-cn": "DHT11/22温湿度传感器",
    "en": "DHT11/22 Temperature and Humidity Sensor"
  },
  "description": {
    "zh-cn": "测量环境温度、湿度",
    "en": "Detect environment temperature and humidity"
  },
  "cover": "cover.png",
  "isDevice": false,
  "sku": "DFR0067|SEN0137",
  "main": "main.js",
  "mode": "upload",
  "libraryConfig": [],
  "meta": {
    "runtimeVersion": "0.0.2"
  },
  "supportArch": [
    "all"
  ]
}
  • 各字段功能说明及注意事项:
字段 说明 类型 注意事项
id 库的唯一编码 string 仅可使用字母数字,不可使用中文或符号或空格,同一个作者的不同库的id需要不同。在Mind+中通过author和id标识一个扩展库,如果重复则会被认定为同一个扩展库。
author 作者名 string 需要使用自己的名称代号,仅可使用字母,不可使用中文或符号空格。在Mind+中通过author和id标识一个扩展库,如果重复则会被认定为同一个扩展库。
version 扩展库的版本号 string 使用三段式数字版本,表示为”大版本号.小版本号.补丁版本“,根据改动大小来调整版本号,例如在0.1.1版本基础上修复bug则使用0.1.2,如果是改动了功能则下一个版本使用0.2.0,如果改动巨大则下个版本号使用1.0.0 ,更多说明:https://semver.org/lang/zh-CN/
name 扩展库中标题 object 至少需要中文和英文标题。过长可能会显示不完整,注意控制长度。zh-cn为中文标题,en为英文标题,其他语言可以增加字段。
description 扩展库描述 object 至少需要中文和英文标题。过长可能会显示不完整,注意控制长度。zh-cn为中文标题,en为英文标题,其他语言可以增加字段。
cover 示意图的路径 string 图片要简单直接的能显示当前扩展库的功能,推荐白色背景,应避免使用与当前主题相同的颜色。图片文件标准尺寸600x372px,非标准尺寸会被拉伸处理。
isDevice 是否为主板扩展 boolean 模块扩展则值为false,主板扩展为true
sku 型号 string 硬件产品型号,数字或字母,多个型号用|隔开,没有则留空
main 入口文件路径 string 编译之后的入口文件名,一般不修改,默认为main.js
meta.runtimeVersion 扩展 runtime 接口版本 string 用于声明该扩展开发时依赖的 runtime API 最低版本。例如:runtime.getBoardPinsByType0.0.2 才支持,则使用该 API 的扩展不应在 0.0.1 的 Mind+ 中加载;同时也不应随意填写过大的版本号,否则会降低扩展兼容性。
mode 所属模式 string 上传模式:upload ,实时模式:realtime ,Python积木模式:python-block ,MicroPython积木模式:micropython-block
libraryConfig 扩展库依赖 array (可选)用于引用全局 Arduino 库
  • 模块扩展(isDevice为false)更多配置项
字段 说明 类型 注意事项
supportDevices 支持的主板 object 指定具体支持的主板{ "deviceId": "version_range" }
isSupportStage 是否支持舞台模式 boolean (可选)仅实时模式可配置此字段。
supportArch 按主板架构进行支持 string[] (可选) 仅 upload 模式可配置此字段:支持的平台架构列表。如 ["arduino:avr", "arduino:samd"];也可为 ["all"] 表示支持所有架构。配置后会覆盖 supportDevices

注:supportArch 中的“架构”取自主板 boardConfig.board 的前两段:例如主板名称为 "arduino:avr:uno",其架构为 "arduino:avr"

修改扩展库信息

  • 我们修改一下信息,例如修改名称,去掉“DHT11/22 ”,保存文件,然后重新在终端运行 npm run build

    注:在终端中按键盘上↑键可以直接填充之前输入的命令,无需每次都手动输入

Pasted image 20260809065314.png

  • 然后重新导入(加载测试扩展后点主界面的“刷新”按钮,或者删除扩展后重新加载config.json)
    Pasted image 20260809065646.png

  • 即可看到效果:
    Pasted image 20260516193724.png

扩展库积木说明:

Pasted image 20260516193848.png

积木定义——index.js

在一个 Mind+ 扩展库中,积木列表的外观、输入参数和文字翻译都定义在 index.js 中;开发者需要具备 JavaScript 基础知识,并了解 ES6 新特性。

  • 主要结构如下:
    //相关环境引用
    import xxxxxxx
    //多语言相关
    setLocaleData(LocaleData)
    
    class YourExtension {
        //构造函数,获取运行时对象,定义生成代码定义
        constructor(runtime, extensionId) {
            this.runtime = runtime;
            this.funcs = new Func(runtime, extensionId);
            this.initPins();
        }
    
        initPins() {
        //从runtime获取主板的引脚列表
        }
        //多语言
        setLocale(locale) {
            setLocale(locale);
            // 切换语言之后, 刷新一次引脚的翻译
            this.initPins();
        }
        //生成代码定义
        getCodePrimitives() {
            return this.funcs;
        }
        //积木定义
        getInfo () {
            // ...
        }
    }
    export default YourExtension;
    

Pasted image 20260613165244.png

  • 示例程序的完整代码和注释如下:

    //引入相关资源文件
    import ArgumentType from '../utils/argument-type';
    import BlockType from '../utils/block-type';
    import DataType from '../utils/data-type';
    import blockIconURI from './icon/blockIcon.svg';
    import menuIconURI from './icon/menuIcon.svg';
    import Func from './func';
    import { setLocaleData, formatMessage, setLocale } from '../utils/translation';
    import LocaleData from "./locales"
    setLocaleData(LocaleData)
    
    //常量定义,方便后续积木中下拉列表的引用
    const DHTVALUE = {
        A: 'DHT11',
        B: 'DHT22'
    };
    
    const TYPEVALUE = {
        A: "temperature",
        B: "moisture"
    }
    
    //定义扩展库的类,注意类名根据当前扩展库名称来命名
    class ExtensionDFR0067 {
        //构造函数,固定名称
        //runtime,运行时环境,核心引擎导入
        //extensionID,扩展ID
        constructor(runtime, extensionId) {
            this.runtime = runtime;
            // Arduino模式的执行方法
            this.funcs = new Func(runtime, extensionId);
            this.initPins();
        }
    
        // 从主板获取引脚列表,方便实现一个扩展库可兼容多个主板
        initPins() {
            // 数字读引脚
            this.digitalReadPins = this.runtime.getBoardPinsByType("DIGITAL_READ", this.device);
            // 数字写引脚
            this.digitalWritePins = this.runtime.getBoardPinsByType("DIGITAL_WRITE", this.device);
            // 模拟读引脚
            this.analogReadPins = this.runtime.getBoardPinsByType("ANALOG_READ", this.device);
            // 模拟写引脚
            this.analogWritePins = this.runtime.getBoardPinsByType("ANALOG_WRITE", this.device);
            // 串口0 rx引脚
            this.serialRxPins = this.runtime.getBoardPinsByType("UART_RX", this.device);
            // 串口0 tx引脚
            this.serialTxPins = this.runtime.getBoardPinsByType("UART_TX", this.device);
            // interrupt引脚
            this.interruptPins = this.runtime.getBoardPinsByType("EXT_INTERRUPT", this.device);
            // 全部引脚
            this.allPins = this.runtime.getBoardPinsByType("ALL", this.device);
    
        }
    
        // 切换多国语言时执行的函数
        setLocale(locale) {
            setLocale(locale);
            // 切换语言之后, 刷新一次引脚的翻译
            this.initPins();
        }
    
        // 固定函数名, Arduino返回block的生成代码方法
        getCodePrimitives() {
            return this.funcs;
        }
    
        // 固定函数名,定义 block 外观和功能,必须有该方法
        getInfo() {
            return {
                //扩展分类名称,显示在积木列表导航栏
                //formatMessage表示引用locales中的多语言翻译的字段
                name: formatMessage({id:"ext.dfr0067.name",default:"DHT11/DHT22"}),
                //引用积木分类图标
                blockIconURI: blockIconURI,
                //引用积木上的图标
                menuIconURI: menuIconURI,
                //定义积木颜色,主体颜色、边框颜色、激活时的颜色
                color1: "#FF9F06",
                color2: "#DD8A04",
                color3: "#BE7705",
                //积木列表配置
                blocks: [
                    {
                        //积木块的编号,每个积木块的编号不可重复,注意此编号上线后后续升级不可修改
                        opcode: 'readDhtValue',
                        //积木类型定义,REPORTER表示为圆形积木块
                        blockType: BlockType.REPORTER,
                        //积木块文字定义,formatMessage表示引用locales中的多语言翻译的字段
                        //id对应locales中json中的键值的key,default为默认值,description为积木功能描述
                        // 用中括号 [] 包围的内容表示输入框
                        text: formatMessage({
                            id: 'gui.blocklyText.dhtTHSensor.readMoistureTemperature',
                            description: 'block msg for readMoistureTemperature',
                            default: 'read pin [PIN] [DHT] [TYPE]'
                        }),
                        // 输入框定义,对应 text 中用中括号包围的字段
                        arguments: {
    
                            PIN: {
                                //type表示输入框的类型,STRING表示文本输入框
                                type: ArgumentType.STRING,
                                //menu表示当前输入框为下拉列表,值为对应后续menus列表中的key
                                menu:"pinMenu",
                                //下拉框的默认值,此处为调用后面定义的_getPinDefault函数获取当前主板默认引脚
                                defaultValue: this._getPinDefault()
                            },
                            DHT: {
                                type: ArgumentType.STRING,
                                menu: 'dhtMenu',
                                //下拉框的默认值,此处为调用前面定义的常量的值
                                defaultValue: DHTVALUE.A
                            },
                            TYPE: {
                                type: ArgumentType.STRING,
                                menu: 'typeValue',
                                //下拉框的默认值,此处为调用前面定义的常量的值
                                defaultValue: TYPEVALUE.A
                            }
                        },
                        outputTypes: [DataType.FLOAT],
                    }
                ],
                menus: {
                    //定义下拉输入框的下拉列表,此处表示使用后续定义的_initPinMenu函数获取的列表
                    pinMenu: this._initPinMenu(),
                    //定义下拉输入框的下拉列表,此处表示提取出前面定义的常量里所有的“值”,并把它们打包成一个数组(Array),等效于['DHT11', 'DHT22']
                    dhtMenu: Object.values(DHTVALUE),
                    //定义下拉输入框的下拉列表,此处表示使用后续定义的_initTypeValue函数获取的列表
                    typeValue: this._initTypeValue()
    
                }
            }
        }
    
        _initPinMenu() {
            //动态从runtime中获取当前选择主板中定义的数字读取引脚列表
            //方便自动适配多种主板
            return this.runtime.getBoardPinsByType("DIGITAL_READ");
        }
    
        _getPinDefault() {
            //从_initPinMenu引用数字读取引脚列表
            const pins = this._initPinMenu();
            // 检查引脚列表,如果不是空的,就使用第一个引脚
            if (pins && pins[0] && pins[0].value) return pins[0].value;
            //如果没有获取到则默认给引脚2
            return "2";
        }
    
    
        _initTypeValue() {
        //返回前面积木需要的兼容多语言的文字,以及下拉框选中时对应的值
            return [
                {
                    text: formatMessage({
                        id: "gui.blocklyText.dhtTHSensor.temperature",
                        description: 'text for typeMenu menu of temperature',
                        default: 'temperature(°C)'
                    }),
                    value: TYPEVALUE.A
                },
                {
                    text: formatMessage({
                        id: "gui.blocklyText.dhtTHSensor.moisture",
                        description: 'text for typeMenu menu of moisture',
                        default: 'moisture(%rh)'
                    }),
                    //value为当此下拉框被选中时返回的值,TYPEVALUE为前面定义的常量
                    value: TYPEVALUE.B
                }
            ]
        }
    }
    //导出类让扩展库引用
    export default ExtensionDFR0067;
    

  • 其中,formatMessage 引用的是 locales 文件夹中各语言文件的字段,例如 zh-cn.json 为简体中文文件,其中有如下字段:

{
  "ext.dfr0067.name": "DHT11/DHT22",
  "gui.blocklyText.dhtTHSensor.readMoistureTemperature": "读取引脚 [PIN] [DHT] [TYPE]",
  "gui.blocklyText.dhtTHSensor.temperature": "温度(°C)",
  "gui.blocklyText.dhtTHSensor.moisture": "湿度(%rh)"
}

生成代码定义——func.js

积木定义完成后,在func.js中定义将积木拖动到工作区后生成代码或执行的内容。

Pasted image 20260613173502.png

  • 下面是示例程序 DHT11 扩展库的生成代码和注释:
//定义生成代码的类,类名对应index.js中import的类名,无需修改
class Func {
    //构造函数,runtime运行时核心引擎导入
    constructor(runtime) {
        this.runtime = runtime
    }
    //readDhtValue这个函数名应该与index.js中对应积木的opcode值相同
    //generator为生成代码构造器
    //block
    //parameter为从积木中获取的生成代码
    readDhtValue (generator,block, parameter) {
        //获取下拉列表的选择值,其中PIN、DHT、TYPE对应index.js中此积木对应的text中下拉框的名称
        let pin = parameter.PIN.code;
        let dht = parameter.DHT.code.toLocaleLowerCase();
        let type = parameter.TYPE.code;
        //根据TYPE下拉框最终收到的value,生成不同的代码
        //${}表示引用变量的字符串值
        let code = type === "temperature" ? `${dht}_${pin}.getTemperature()` : `${dht}_${pin}.getHumidity()`;
        //生成代码定义
        //定义include区域的生成代码
        generator.addInclude('DFRobot_DHT.h');
        //定义对象定义区域生成的代码,id和内容
        generator.addObject(`${dht}_${pin}`, `DFRobot_DHT ${dht}_${pin};`);
        //定义初始化区域生成的代码,id和内容
        generator.addSetup(`${dht}_${pin}.begin`, `${dht}_${pin}.begin(${pin}, ${dht.toUpperCase()});`);
        //定义积木位置生成的代码,内容和优先级
        return ([code, generator.ORDER_ATOMIC]);
    }
}
//导出类让扩展库引用
export default Func;

增加积木实操

此处我们尝试给这个扩展库增加一个测试用的积木,积木外观示意如下:

【测试积木 下拉列表[项1/项2/项3] 输入框("hello")】

操作流程为:

  • 找到index.js
  • 在最开始增加下拉列表的常量

    const ITEMVALUE = {
        ITEM1: "item1",
        ITEM2: "item2",
        ITEM3: "item3"
    }
    

  • 然后定位到 blocks,添加这个积木的定义

    ,
                    {
                        opcode: 'testBlock',
                        blockType: BlockType.COMMAND,
                        text: formatMessage({
                            id: 'gui.blocklyText.dhtTHSensor.testBlock',
                            description: 'test block',
                            default: 'Test Block Drop-down List [ITEM] Input Box [INPUT]'
                        }),
                        arguments: {
                            ITEM:{
                                type: ArgumentType.STRING,
                                menu: 'testItem',
                                default: ITEMVALUE.ITEM1
                            },
                            INPUT:{
                                type: ArgumentType.STRING,
                                defaultValue:  "hello"
                            }
                        },
                        outputTypes: [DataType.FLOAT],
                    }
    

  • 然后在 menus 中增加下拉列表的定义

                menus: {
                    pinMenu: this._initPinMenu(),
                    dhtMenu: Object.values(DHTVALUE),
                    typeValue: this._initTypeValue(),
                    testItem: Object.values(ITEMVALUE)  /* 新增 */
    
                }
    
    Pasted image 20260613190008.png

  • 修改完成后保存文件,重新编译并刷新,即可看到内容。 Pasted image 20260613185825.png

  • 接下来处理多语言显示

  • 打开locales文件夹,打开zh-cn.json(其他语言打开对应语言的json文件),添加一条字段

    ,
      "gui.blocklyText.dhtTHSensor.testBlock": "测试积木 下拉列表[ITEM] 输入框 [INPUT]"
    

  • 重新编译后刷新,可以看到积木可以显示中文了,但是下拉列表还是英文
    Pasted image 20260613190439.png

  • 继续修改index.js,将下拉列表引用改为使用多语言

  • 新建一个_initItemValue函数处理下拉框为多语言

        _initItemValue() {
            return [
                {
                    text: formatMessage({
                        id: "gui.blocklyText.dhtTHSensor.item1",
                        description: 'test block item1',
                        default: 'item1'
                    }),
                    value: ITEMVALUE.ITEM1
                },
                {
                    text: formatMessage({
                        id: "gui.blocklyText.dhtTHSensor.item2",
                        description: 'test block item2',
                        default: 'item2'
                    }),
                    value: ITEMVALUE.ITEM2
                },
                {
                    text: formatMessage({
                        id: "gui.blocklyText.dhtTHSensor.item3",
                        description: 'test block item3',
                        default: 'item3'
                    }),
                    value: ITEMVALUE.ITEM3
                }
            ]
        }
    

  • menus中改为引用这个函数

    menus: {
                    pinMenu: this._initPinMenu(),
                    dhtMenu: Object.values(DHTVALUE),
                    typeValue: this._initTypeValue(),
                    testItem: this._initItemValue() /* 调整 */
    
                }
    

  • zh-cn.json中增加下拉菜单的翻译

    ,
      "gui.blocklyText.dhtTHSensor.item1": "项1",
      "gui.blocklyText.dhtTHSensor.item2": "项2",
      "gui.blocklyText.dhtTHSensor.item3": "项3"
    

Pasted image 20260613191220.png

  • 修改完成后保存文件,重新编译并刷新,即可看到下拉列表也变为中文。 Pasted image 20260613191058.png

  • 此时将积木拖到工作区,会发现生成代码是"undefined;",这是因为我们还未定义积木生成代码 Pasted image 20260613191411.png

  • 打开func.js,增加一个积木opcode同名的testBlock函数

        testBlock (generator,block, parameter) {
    
        }
    

  • 接下来我们让这个积木根据输入框生成一些代码
        testBlock (generator,block, parameter) {
            //获取输入框对象生成的代码
            let item = parameter.ITEM.code;
            let input = parameter.INPUT.code;
            //拼接成一个字符串存入变量
            let code = `${item}.${input}`;
            //指定生成任意位置代码
            return (code);
    
        }
    
  • 保存代码重新编译后刷新扩展库,可以看到代码已经正常生成了。 Pasted image 20260613191949.png

至此,你已经了解扩展库关键文件的作用以及开发流程。更多关于积木定义、下拉框定义和 runtime 接口定义的内容,请查阅本文档中 各模式扩展库开发文档和通用知识文档

内部测试

扩展库开发自测完成后,如果想发给其他人进行内部测试,可以按以下步骤操作:

  • 打开 build 文件夹,其中是当前编译后的文件。将该文件夹压缩后发送给测试方,对方解压后打开开发模式并导入 config.json,即可加载。
  • 注意:由于开发阶段的扩展库暂未经过上线,可能存在问题反复修改,为避免形成无法使用的过渡性项目文件,在开发者模式的扩展库不能保存项目文件,仅可以进行测试。

Pasted image 20260809072537.png

扩展库上线

经过自测和内测后,如果确认无问题并准备正式使用,则需要申请官方收录。
为了保证扩展库的稳定性和可维护性,需要使用 Git 进行管理,并托管到 Gitee(码云)或 GitHub。国内推荐使用 Gitee,访问更稳定。

Gitee 初始设置

  • 首先前往 Gitee 创建账号:https://gitee.com
    Pasted image 20260613194622.png

  • 登录 Gitee 后,在右上角头像下拉菜单中点击“设置”,完成以下设置:

    • 邮箱管理>新增邮箱>绑定个人邮箱,本地推送代码时需要设置邮箱
    • 账号管理>实名认证>进行实名认证,否则仓库无法设置为公开
    • 账号管理 > 第三方账号绑定 > 扫码绑定微信,可以使用微信登录。
  • 绑定 SSH key,将本地电脑的公钥添加到 Gitee 账号后,后续可以直接从电脑向 Gitee 推送代码,无需每次输入账号密码。

    • 在桌面右键菜单中选择“Open Git GUI here”打开 Git GUI。
    • 点击 Help > Show SSH Key,会显示当前的 SSH key。
      Pasted image 20260613195421.png

    • 如果为空,则点击“Generate Key”。随后会弹出“Enter passphrase”提示框,无需输入,直接点击“OK”;再次弹出“Enter same passphrase again”时,同样直接点击“OK”。生成当前电脑的 SSH key 后,点击“Copy To Clipboard”复制。
      Pasted image 20260613195957.png Pasted image 20260613195917.png

    • 回到 Gitee 设置中的“安全设置”>“SSH 公钥”>“添加公钥”,将刚才复制的 SSH key 粘贴到“公钥”输入框中,点击确定。
      Pasted image 20260613200134.png Pasted image 20260613200211.png

本地仓库 Git 初始化

  • 接下来在前面使用的 VS Code 中切换到“源代码管理”卡片,然后点击“初始化仓库”。
    Pasted image 20260613193636.png

  • 点击“暂存所有更改” 。
    Pasted image 20260613193819.png

  • 内容会进入本地“暂存区”,然后在输入框中输入本地修改内容的备注,之后点击“提交”。

user.name 和 user.email 配置

  • 如果是新电脑第一次提交,系统会提示配置 user.name 和 user.email,说明本地尚未配置 Git 用户名和邮箱。
    Pasted image 20260613193939.png
    Pasted image 20260613194056.png

  • 配置 Git 用户名和邮箱。

  • 打开 VS Code 终端,或者在桌面右键选择“Open Git Bash here”打开 Bash 终端。
  • 复制 Gitee“个人主页”上显示的 @ 后面的用户名,以及邮箱管理中的提交邮箱。
    git config --global user.name "你的用户名"
    git config --global user.email "你的邮箱地址"
    

Pasted image 20260613201233.png
Pasted image 20260613203827.png

  • 打印当前的配置,显示了刚才输入的配置即说明设置正常
    git config --global --list
    

Pasted image 20260613204247.png

  • 然后测试连接:输入如下命令,提示后输入“yes”并回车。若显示 successfully,则说明当前电脑与 Gitee 的连接正常,可以通过 SSH 推送代码。
    ssh -T git@gitee.com
    

Pasted image 20260613204408.png

本地 Git 提交

  • 配置完成后,回到 VS Code,再次点击“提交”,此时不会弹出未设置用户名的提示,而是直接提交成功;下方“图表”中会显示刚才提交时设置的备注。 Pasted image 20260613201406.png

推送到 Gitee 云端

云端建立仓库

在 Gitee 上创建一个仓库,仓库名称以 ext2- 开头,后面跟上当前模块的名称,不要使用中文或空格,例如 ext2-dht11。路径会自动生成,填写简短的仓库介绍,其他选项无需修改,点击“创建”。
Pasted image 20260613201759.png

  • 创建完成后会进入初始界面,选择“SSH”,然后点击“复制”按钮。
    Pasted image 20260613202007.png

绑定本地仓库

  • 打开 VS Code 的源代码管理器,点击“远程”>“添加远程存储库”>粘贴刚才的 Git 链接并回车>输入 origin 并回车。 Pasted image 20260613204839.png

推送到云端

  • 此时点击“发布 Branch”按钮,稍等几秒后按钮变成“提交”,说明提交完成。打开 Gitee 网页并刷新,即可看到代码已经推送到 Gitee。 Pasted image 20260613205056.png

如果其他人无法访问该链接,可能是当前仓库为私有仓库。可以点击“管理”>“基本信息”,将“是否开源”设置为“开源”。

Pasted image 20260613205122.png

提交收录

如果仓库开发完毕,测试无问题,需要正式使用,则可以提交收录。

检查仓库内容

提交前应该先检查在线仓库,确保以下关键事项:

  1. build编译通过,在Mind+中导入正常使用,经过实测;
  2. config.json中的id、author、version符合规范;
  3. 仓库中的 README.md文件 ,应该将当前扩展库介绍、积木说明、使用示例、版本变更记录补全。

提交邮件

给 Mind+ 官方邮箱(mindplus@dfrobot.com)发送邮件:

  • 标题写:Mind+ V2 扩展库上架 + 仓库名称。
  • 正文中附上类型、gitee仓库链接、config.json的完整内容

注:如果后续有新版本更新,则以同样的方式发送,并在邮件中注明“版本更新”即可。

Pasted image 20260613210439.png

后续扩展库版本迭代

注意事项

如果第一个版本已经被 Mind+ 扩展库收录,后续更新此扩展库功能时需要注意以下几点:

  • config.json 中的 version 字段需要升级,小改动升级小版本号,大改动升级大版本号,数字依次递增。
  • config.json 中的 id、author 不可修改。
  • index.js 中积木的 opcode 不可修改,arguments 的数量和 key 也不可修改。
  • 新版本需要向下兼容,也就是老版本扩展库中的所有积木保存的项目文件,都可以使用新扩展库正常加载。
  • 每次升级版本时,需要将本地修改的主要变更内容记录在 README.md 中,方便使用者了解。

流程

升级版本号 > 更新内容 > 导入 Mind+ 本地测试 > 发给第三方测试(如有) > 推送到 Gitee > 发送邮件通知


更多关于积木定义、下拉框定义和 runtime 接口定义的内容,请查阅本文档中 后续章节
加入 Mind+官方交流群 可交流扩展库开发经验。