Serial Studio 输出控件(Output Controls)完整指南:用 JavaScript 让仪表盘向设备发送命令

发布时间:2026/9/18 22:30:59
Serial Studio 输出控件(Output Controls)完整指南:用 JavaScript 让仪表盘向设备发送命令 Serial Studio 输出控件Output Controls完整指南用 JavaScript 让仪表盘向设备发送命令【免费下载链接】Serial-StudioOpen-source telemetry dashboard. Supports UART, BLE, MQTT, Modbus, CAN Bus and more.项目地址: https://gitcode.com/GitHub_Trending/se/Serial-Studio本指南围绕 Serial Studio 的 Output Controls 输出控件展开从控件类型、transmit 函数编写、协议辅助函数到状态反馈与布局规则完整讲解如何让遥测仪表盘不仅能“读”设备数据还能向设备发送命令、设定值与参数。读完本文你将掌握 Button/Slider/Toggle/Text Field/Knob 五类交互控件的配置方法能够编写跨协议复用的transmit(value)脚本并借助内置 Modbus、CAN Bus 辅助函数构造二进制报文。概览什么是输出控件Serial Studio 的标准控件负责将传入的遥测数据可视化而**输出控件Output Controls**是与之互补的交互式仪表盘组件负责把数据发回给已连接的设备。它们传输命令、设定值和参数使仪表盘既能从设备读取数据也能向设备写入数据。每个输出控件的核心是一个用户自定义的 JavaScripttransmit(value)函数它把控件交互点击按钮、拖动滑块、输入文本转换为设备期望的精确字节。由于传输逻辑完全由 JavaScript 决定输出控件是协议无关的同一个滑块控件只要更换transmit函数就能驱动纯文本串口命令、JSON 载荷或二进制数据包。注意输出控件需要Pro 许可证。源码 core/Ui/UI/Widgets/Output/Base.cpp 中deliverValue()在评估脚本之前先检查Licensing::CommercialToken与许可证守卫未激活时直接返回不发送任何数据。输出控件的工作原理输出控件的交互流程可以概括为四步用户在仪表盘上操作控件点击按钮、拖动滑块、输入文本等控件将交互值传给其 JavaScripttransmit(value)函数函数返回一个字符串二进制载荷是使用String.fromCharCode构造的字节串Serial Studio 将结果发送给已连接的设备。传输过程有速率限制连续两次发送之间至少间隔 50 ms防止滑块拖动等连续交互时设备缓冲区溢出。这一机制在 Base.h 中以m_rateLimiterQElapsedTimer与m_flushTimerQTimer实现见Base::sendValue()落在限速窗口内的调用不会丢弃而是暂存为m_pendingValue等窗口结束后以最新值发送——因此拖动滑块时快速变化的中间值会被折叠最终停留在松开鼠标时的位置。传输安全边界evaluateTransmitFunction()见 Base.cpp在源码层面落实了文档中的三条硬性约束返回值必须是字符串非字符串结果按toByteArray()处理返回普通数字数组不会发送任何数据载荷上限 65536 字节超过kMaxPayloadBytes的载荷会被中止kMaxPayloadBytes 65536500 ms 执行超时transmit()调用超过kTransmitWatchdogMs 500毫秒会被JsWatchdog看门狗中止kTransmitWatchdogMs 500。以上两种情况都会中止发送并在控件上闪烁红色边框鼠标悬停可查看具体错误信息。输出控件类型输出控件共有五种类型各自把不同类型的值传入transmit()。Button按钮单击发送单条命令启用Toggle Button闩锁按钮后可在按下/释放两种状态间切换。属性传入transmit()的值普通按钮1整数闩锁按钮按下1/ 释放0适用场景复位、启动/停止、触发测量、继电器和使能线。属性默认值说明Button Icon无标题旁显示的图标Colorize Icon关用按钮颜色为图标着色Button Color自动自定义填充色自动时使用分组强调色Button SizeNormalSmall、Normal、Large 或 Extra LargeToggle Button关在点击之间保持按下状态发送1开/0关On Label空闩锁时显示的标题为空时回退到控件标签Off Label空释放时显示的标题为空时回退到控件标签闩锁按钮在开启时以按钮颜色填充、关闭时显示为普通按钮因此无需打开设备即可读出状态。它发送的值与 Toggle 完全相同为一个控件编写的transmit函数可以直接用于另一个。Button Size会同时缩放按钮、图标和标题面板打包器会为它预留成比例更大的单元格大按钮不会被相邻控件裁剪。设置自定义Button Color时标题会在黑色和白色之间自动切换以保证可读性。Slider滑块从可拖动滑块发送数值。属性默认值说明Min Value0滑块范围下限Max Value100滑块范围上限Step Size1离散位置之间的增量Initial Value0起始位置传入transmit()的值是钳制在 [Min, Max] 范围内的数字。拖动过程中持续发送受 50 ms 速率限制。Toggle开关二值开/关开关。属性默认值说明Initial Value0起始状态0 关1 开打开时向transmit()传入1关闭时传入0。Text Field文本输入框接受任意文本输入并作为字符串发送。属性传入transmit()的值交互方式按 Enter 或点击 Send传入值输入的字符串适用场景AT 命令、调试控制台、自定义查询。Knob旋钮用于连续设定值调节的旋转拨盘。数值属性与 Slider 相同Min、Max、Step、Initial Value但以圆形拨盘形式显示在面板布局中属于“高控件”会占满整个列高。创建输出控件在 Project Editor工具栏扳手图标中按以下步骤创建打开 Project Editor点击工具栏中的Add Output按钮之一Button、Slider、Toggle、Text Field 或 Knob如果不存在Output Panel分组会自动创建一个在树视图中选中新控件配置属性然后点击工具栏中的Edit Code编写其 transmit 函数。输出控件位于Output Panel分组内。你也可以先通过工具栏添加 Output Panel 分组再向其中添加控件。每个 Output Panel 可容纳多个混合类型控件根据可用宽度自动打包成尽可能多的列。从源码结构看五种控件分别对应 core/Ui/UI/Widgets/Output 目录下的Button.cpp、Slider.cpp、Toggle.cpp、TextField.cpp旋钮控件与滑块共用Slider的数值通道面板布局由Panel.cpp/PanelLayout.cpp负责。transmit 函数详解每个输出控件都有一个 JavaScripttransmit(value)函数决定交互如何变成设备命令。该函数在仪表盘打开时编译一次在每次交互时执行。编写 transmit 函数在 Project Editor 中选中控件并点击工具栏Edit Codetransmit 函数会在独立窗口中打开窗口头部提供模板列表、导入、校验和测试功能代码旁有控件的实时预览状态栏会随着输入实时报告脚本是否编译成功、是否定义了transmit(value)。无法编译或从未定义transmit(value)的脚本不会被保存项目会保留上一个可用的版本关闭窗口时若脚本损坏会先询问是否放弃。预览功能驱动真实的控件使用其自身的范围、步长和标签并显示脚本产生的精确字节。预览永远不会真正发送——不会有任何数据到达已连接的设备因此即使挂着硬件也可以安全地演练继电器或 PWM 脚本。这一安全性由 TransmitScriptEnvironment.cpp 保证非 Live 表面会以惰性记录器kInertBridges替换所有有副作用的桥接deviceWrite、actionFire、apiCall、tableSet、mqttPublish等并安装探针kInertProbe验证替换确实生效后才允许编译。函数接收单个value参数必须返回字符串function transmit(value) { // value 的取值 // 1 Button 点击 // 0 或 1 Toggle 状态变化及闩锁 Button // number Slider 和 Knob // string TextField 输入 return CMD value \r\n; }返回值必须是字符串。对于二进制协议用String.fromCharCode(...)构造字节串见 Binary Packet 模板返回普通数字数组不会发送任何数据。载荷上限 65536 字节单次transmit()调用超过 500 ms 会被看门狗终止——两种情况都会中止发送并让控件闪烁红色边框悬停可查看错误信息。内置模板代码编辑器内置一组开箱即用的模板Simple command、JSON command、Binary packet、PWM control、PID setpoint、Relay toggle、AT command、Modbus write、CAN Bus frame、G-Code command、GRBL command、NMEA sentence、SCPI command、SLCAN command 和 Default template。从模板下拉框选择一个并针对自己的设备定制即可。这些模板的真实实现位于 app/rcc/scripts/output 目录如simple_command.js、json_command.js、binary_packet.js、pwm_control.js、pid_setpoint.js、relay_toggle.js、at_command.js、modbus_write.js、canbus_frame.js、gcode_command.js、grbl_command.js、nmea_sentence.js、scpi_command.js、slcan_command.js、default_template.js。Simple Command简单命令发送带行终止符的纯文本自动适配控件类型。function transmit(value) { if (typeof value string) return value \r\n; if (value 1) return ON\r\n; if (value 0) return OFF\r\n; return SET value \r\n; }JSON CommandJSON 命令发送结构化 JSON 对象适合固件解析 JSON 输入的场景。function transmit(value) { var obj { cmd: set, value: value, ts: Date.now() }; return JSON.stringify(obj) \n; }Binary Packet二进制数据包发送带 STX/ETX 分隔符的成帧二进制数据。function transmit(value) { var STX String.fromCharCode(0x02); var ETX String.fromCharCode(0x03); var cmd String.fromCharCode(0x01); var val String.fromCharCode(Math.round(value) 0xFF); // XOR checksum over cmd value var chk String.fromCharCode(0x01 ^ (Math.round(value) 0xFF)); return STX cmd val chk ETX; }PWM ControlPWM 控制发送占空比值0-255用于电机调速、LED 亮度或加热器控制。var CHANNEL 0; function transmit(value) { var duty Math.round(Math.max(0, Math.min(255, value))); return PWM CHANNEL duty \r\n; }注意CHANNEL声明在transmit()之外——脚本顶层变量在多次调用之间保持存在见下文“Tips”。PID SetpointPID 设定值为 PID 控制器发送带 2 位小数的浮点设定值。var SP_MIN 0.0; var SP_MAX 100.0; function transmit(value) { var sp Math.max(SP_MIN, Math.min(SP_MAX, Number(value))); return SP sp.toFixed(2) \r\n; }Relay Toggle继电器切换为继电器或数字输出发送不同的 ON/OFF 命令。var CHANNEL 0; function transmit(value) { var state value ? ON : OFF; return RELAY CHANNEL state \r\n; }AT CommandAT 命令为调制解调器、蓝牙模块和 WiFi 模块发送 AT 风格命令。function transmit(value) { if (typeof value string value.length 0) return AT value \r\n; return AT\r\n; }Modbus Register WriteModbus 寄存器写入使用内置辅助函数将滑块值直接写入 Modbus 保持寄存器。function transmit(value) { return modbusWriteRegister(0x0001, value); }CAN Bus FrameCAN 总线帧使用内置辅助函数将数值作为 CAN 帧发送。function transmit(value) { return canSendValue(0x100, value, 2); }从文件导入点击 transmit 函数窗口头部的Import可从磁盘加载.js文件便于跨项目共享 transmit 函数或单独进行版本控制。状态反馈State Feedback默认情况下输出控件显示的是你最后一次设置的状态。但设备可能自行变化联锁跳闸、有人使用本地面板、命令被拒绝。此时控件仍显示你最后一次点击的结果向操作员传达的是与机器实际状态不符的信息。将控件绑定到**状态源state source**后控件显示的是设备报告的状态。在 Project Editor 中选中控件将State Source设置为某个数据集或表格变量然后选择具体的数据源。负载组load bank是一个典型场景停止负载组也会停止其冷却风扇因为二者在设备侧而非 Serial Studio 中互锁。将风扇控件绑定到携带风扇状态的数据集负载组一旦停机风扇控件立即不再声称风扇在运行。On Value判定为“开”的值对于 Toggle 或闩锁按钮On Value决定哪些读数表示“开”。留空时任何非零数字都表示“开”。这适用于数据集报告1和0的情况但报告文本如RUN和STOP的设备不携带数字此时即使设备正在运行也会被读作“关”——填入RUN后控件会精确匹配它并自动以文本或数字方式进行比较。更复杂的情况状态字中的某个位、阈值、滞回应交给数据集自身的变换transform处理这正是它的用途。在那里产生干净的0或1然后让On Value保持为空。等待与无数据Waiting and No Data绑定后的控件显示三种状态而非两种live实时数据源正在报告控件显示其报告的内容waiting…等待中你操作了但设备尚未确认。Confirm Within (ms)决定这个窗口持续多久——接触器可能在不到一秒内闭合而风扇需要数秒。在此窗口内控件持续显示你请求的内容反馈不会覆盖它no data无数据数据源未报告或已静默。控件变暗并明确提示因为把“我们没听到”显示成“关”正是该功能要消除的失败。反馈永远不会发送反映设备状态不是操作员动作因此绑定控件不可能在循环中命令自身。拖动已绑定的滑块也永远不会被打断反馈被暂时挂起直到你松开为止。从源码看状态判定逻辑位于 StateBinding.cppBase::refreshState()每个显示周期都会重新计算判定Verdict因为“等待确认”窗口是按时钟而非按采样到达与否推进的——设备在命令后静默也必须停止显示“waiting”。observeState()是外部值进入控件的唯一通道且刻意不接触发送目标从机制上杜绝了命令自身的环路见 Base.cpp 的注释对应 spec 0080 R3。如果你删除了控件绑定的数据集或变量项目仍能加载、控件仍能工作——只是不再自我校正并且 Problems 列表会报告该问题。协议辅助函数每个输出控件的 JavaScript 引擎都内置了 Modbus 和 CAN Bus 协议辅助函数app/rcc/api/prelude.js。它们处理二进制字节打包无需手动构造原始字节。Modbus 辅助函数所有 Modbus 辅助函数都接受一个可选的尾随unit参数1 到 247。给出该参数时载荷会以前缀0xFF 0x83 unit开头Modbus 驱动将写入该设备而非连接自身的 unit——这正是控件指挥共享 RS-485 总线上第二个设备的方式见__ssModbusUnit的实现prelude.js越界 unit 会被忽略并回退到连接自身 unit。由于辅助函数返回字节串请将控件的传输编码设置为Latin-1避免 127 以上的字节被重新编码。modbusWriteRegister(address, value, unit)将 16 位整数写入单个保持寄存器。参数类型说明addressNumber寄存器地址0x0000–0xFFFFvalueNumber要写入的值取整为整数0–65535function transmit(value) { return modbusWriteRegister(0x0001, value); }实现上该函数将地址和值各截断为 16 位并大端打包为 4 字节[addr_hi, addr_lo, value_hi, value_lo]。modbusWriteCoil(address, on, unit)按 ON/OFF 约定ON 0xFF00OFF 0x0000写入保持寄存器。此辅助函数目前是modbusWriteRegister()的别名它打包相同的 4 字节保持寄存器写入并不发出原生 Modbus 线圈写入功能码 5/15——驱动始终以保持寄存器为目标功能码 6/16。当下游 PLC 程序将该保持寄存器映射到物理继电器或线圈时使用它它无法用于期望真正线圈写入的设备。参数类型说明addressNumber保持寄存器地址0x0000–0xFFFFonBoolean/Number真值 ON假值 OFF// Toggle 控件向 PLC 映射到继电器的保持寄存器写入 ON/OFF function transmit(value) { return modbusWriteCoil(0x0000, value); }其源码实现即modbusWriteRegister(address, on ? 0xFF00 : 0x0000, unit)见 prelude.js。modbusWriteFloat(address, value, unit)跨两个连续的保持寄存器大端写入 IEEE-754 32 位浮点数。参数类型说明addressNumber起始寄存器地址valueNumber浮点数值// 滑块以 32 位浮点数写入温度设定值 function transmit(value) { return modbusWriteFloat(0x0010, value); }实现使用DataView.setFloat32(0, value, false)得到大端字节序再拼接寄存器地址后输出 6 字节载荷。此外prelude.js 还提供了modbusWriteRegisters(address, values, unit)用于将一组 16 位值写入连续多个寄存器。CAN Bus 辅助函数canSendFrame(id, payload)发送具有指定标识符和载荷的任意 CAN 帧。参数类型说明idNumberCAN 标识符payloadArray 或 String载荷字节0–255 的数字数组或原始字符串// Button 发送固定命令帧 function transmit(value) { return canSendFrame(0x200, [0x01, 0x00, 0xFF]); }// Slider 将值打包进 3 字节载荷 function transmit(value) { var v Math.round(value); return canSendFrame(0x100, [0x01, (v 8) 0xFF, v 0xFF]); }从实现看id 0x7FF时按扩展帧29 位加 EFF 标志位打包否则按标准帧11 位打包并自动附带 DLC 长度字节见 prelude.js。canSendValue(id, value, bytes)将数值按大端序打包进 CAN 帧。参数类型默认值说明idNumber必填CAN 标识符valueNumber必填数值取整为整数bytesNumber2载荷字节数1–8// Slider 在 CAN ID 0x100 上发送 16 位值 function transmit(value) { return canSendValue(0x100, value, 2); }// Knob 在 CAN ID 0x300 上发送 32 位值 function transmit(value) { return canSendValue(0x300, value, 4); }组合辅助函数与自定义逻辑辅助函数返回字符串因此可以拼接或按条件选择// 根据开关状态写入不同寄存器 function transmit(value) { if (value) return modbusWriteRegister(0x0010, 1); // Enable else return modbusWriteRegister(0x0010, 0); // Disable }// 发送带头部字节和控件值的 CAN 帧 function transmit(value) { return canSendFrame(0x150, [0xAA, Math.round(value) 0xFF]); }Output Panel 布局输出控件显示在仪表盘的 Output Panel 控件中面板使用自适应布局引擎控件根据可用宽度自动打包成尽可能多的列列数由每个控件的最小宽度推导小控件Button、Slider、Toggle、TextField在列内垂直堆叠高控件Knob占满整个列高控件超出可见区域时面板垂直滚动。相关实现见 Panel.cpp 与 PanelLayout.cpp。多数据源项目在具有多个数据源设备的项目中目标设备由控件所属Output Panel 分组的数据源决定而非某个控件属性。组内每个控件都发送到该组的数据源。要发送到不同设备请将控件放入分配给该数据源的 Output Panel 分组。从源码看每个控件通过sourceId与目标绑定Base.h 中的Q_PROPERTY(int sourceId)该值来自DataModel::OutputWidget::sourceId即所属分组对应的设备源。输出控件与 Actions 的对比输出控件和 Actions 都能向已连接设备发送数据但用途不同特性输出控件Actions控件类型5 种按钮、滑块、开关、文本框、旋钮仅按钮数据格式化JavaScripttransmit()函数固定 TX Data EOL连续值支持滑块、旋钮不支持定时器/自动重复不支持支持5 种定时模式连接时自动执行不支持支持许可证Pro免费使用 Actions的场景简单的 fire-and-forget 命令、周期性轮询、连接时自动执行序列。使用输出控件的场景需要带连续值的交互式控件、自定义数据格式化或混合控件类型。实战示例电机速度控制器用滑块控制电机速度加一个急停按钮。控件类型属性SpeedSliderMin: 0, Max: 100, Units: %Emergency StopButton速度 transmit 函数function transmit(value) { return SPD Math.round(value) \r\n; }急停 transmit 函数function transmit(value) { return ESTOP\r\n; }继电器控制面板独立切换 3 个继电器。控件类型属性Relay 1ToggleON: Closed, OFF: OpenRelay 2ToggleON: Closed, OFF: OpenRelay 3ToggleON: Closed, OFF: Open每个继电器使用带各自继电器编号的定制 transmit 函数// Relay 1 function transmit(value) { return value ? R1 ON\r\n : R1 OFF\r\n; }传感器校准界面组合文本字段命令与旋钮微调。控件类型属性CommandTextFieldOffsetKnobMin: -10, Max: 10, Step: 0.1, Units: mV命令 transmit 函数function transmit(value) { return CAL value \r\n; }偏移 transmit 函数function transmit(value) { return OFFSET Number(value).toFixed(1) \r\n; }Modbus PID 控制器通过 Modbus 控制 PID 回路向保持寄存器写入设定值、Kp 和使能/禁用。控件类型属性SetpointSliderMin: 0, Max: 500, Step: 0.5, Units: °CKp GainKnobMin: 0, Max: 10, Step: 0.01EnableToggleON: Running, OFF: Stopped设定值 transmit 函数32 位浮点写入寄存器 0x0010–0x0011function transmit(value) { return modbusWriteFloat(0x0010, value); }Kp Gain transmit 函数32 位浮点写入寄存器 0x0012–0x0013function transmit(value) { return modbusWriteFloat(0x0012, value); }Enable transmit 函数通过modbusWriteCoil别名写入保持寄存器 0x0000下游映射到继电器function transmit(value) { return modbusWriteCoil(0x0000, value); }CAN 总线电机控制器通过 CAN Bus 控制电机速度设定值与急停。控件类型属性SpeedSliderMin: 0, Max: 10000, Units: RPMDirectionToggleON: Forward, OFF: ReverseE-StopButton速度 transmit 函数CAN ID 0x100 上的 16 位值function transmit(value) { return canSendValue(0x100, value, 2); }方向 transmit 函数CAN ID 0x101 上的单字节function transmit(value) { return canSendFrame(0x101, [value ? 0x01 : 0x00]); }急停 transmit 函数CAN ID 0x1FF 上的固定命令帧function transmit(value) { return canSendFrame(0x1FF, [0xFF, 0x00]); }常见错误排查控件未出现在仪表盘上症状输出控件已在 Project Editor 中配置但仪表盘上不显示。修复确保设备已连接——Output Panel 仅在连接激活时显示在仪表盘上。同时确认控件位于 Output Panel 分组内分组类型必须为 Output。设备收不到命令症状控件可见且可交互但设备无响应。修复检查Console视图确认数据确实在发送验证 transmit 函数返回了正确终止的字符串多数设备期望\r\n在多数据源项目中确认控件位于分配给正确设备的 Output Panel 分组内检查 Pro 许可证是否激活——未激活时传输被禁用。滑块发送太多命令症状拖动滑块时设备过载或串口缓冲区溢出。修复内置的 50 ms 速率限制能阻止大多数刷屏但如果设备需要更长的命令间隔可以增大步长以减少离散值数量或在 transmit 函数中加入去抖逻辑。transmit 函数错误症状控件显示红色文本 No transmit function defined 而非控件本身。修复打开 Project Editor 检查 transmit 函数语法错误。函数必须是名为transmit的有效 JavaScript 函数接受一个参数并返回字符串。字段为空时也会显示此标签。transmit 期间的运行时错误看门狗超时、脚本异常或载荷过大会让控件闪烁红色边框数秒悬停可读取错误信息。实用技巧从内置模板开始修改避免常见语法错误打开 Console 视图测试观察实际发送的字节在同一仪表盘上组合输出控件与输入控件实现完整的闭环监控例如用滑块设定目标温度同时用仪表显示实际温度优先使用内置协议辅助函数modbusWriteRegister、canSendFrame等而非手工打包二进制字节对于超出内置辅助函数的协议在脚本中transmit()旁边定义自己的辅助函数。在transmit()外部声明的变量在多次调用之间持续存在如 PWM 模板中的CHANNEL。相关文档SDK Referencetransmit()返回的协议编码器modbusWriteRegister、canSendFrame等及更广泛的脚本接口Actions带定时器支持的简单命令按钮Project Editor创建和配置项目的完整指南Toolbar Button ReferenceProject Editor 的添加控件按钮及应用其余界面元素Widget Reference所有输入与可视化控件类型Frame Parser ScriptingLua 和 JavaScript 解析器参考Data Sources配置设备连接。【免费下载链接】Serial-StudioOpen-source telemetry dashboard. Supports UART, BLE, MQTT, Modbus, CAN Bus and more.项目地址: https://gitcode.com/GitHub_Trending/se/Serial-Studio创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考