UNPKG

@liball/node-red-contrib-opi-gpio

Version:

Orange Pi GPIO. Digital input/output for Orange Pi

273 lines (245 loc) 13.8 kB
<script type="text/html" data-template-name="opi_in"> <div class="form-row"> <label for="node-input-name"><i class="icon-tag"></i> 名称</label> <input type="text" id="node-input-name" placeholder="例如:读取按钮状态"> </div> <div class="form-row"> <label for="node-input-select"><i class="fa fa-microchip"></i> 设备型号</label> <select id="node-input-select" style="width: 70%;"> <option value="" disabled>-- 选择设备型号 --</option> <option value="opz2w">Orange Pi Zero 2W</option> </select> </div> <div class="form-row" id="device-pinout-image-container" style="text-align: center; display: none; margin-top: 10px; border: 1px dashed #ccc; padding: 5px;"> <label style="display: block; margin-bottom: 5px;">引脚布局图:</label> <img id="device-pinout-image" src="" alt="设备引脚图" style="max-width: 95%; height: auto;"/> </div> <div id="opz2w-pin-options" style="display: none;"> <div class="form-row"> <label for="node-input-pin-opz2w"><i class="fa fa-tag"></i> GPIO 引脚</label> <select id="node-input-pin-opz2w" style="width: 70%;"> <option value='' disabled selected style='display:none;'>选择引脚 或 自定义</option> <option value="264">PI8 (264)</option> <option value="263">PI7 (263)</option> <option value="269">PI13 (269)</option> <option value="224">PH0 (224)</option> <option value="225">PH1 (225)</option> <option value="257">PI1 (257)</option> <option value="226">PH2 (226)</option> <option value="227">PH3 (227)</option> <option value="261">PI5 (261)</option> <option value="270">PI14 (270)</option> <option value="228">PH4 (228)</option> <option value="231">PH7 (231)</option> <option value="232">PH8 (232)</option> <option value="262">PI6 (262)</option> <option value="230">PH6 (230)</option> <option value="229">PH5 (229)</option> <option value="233">PH9 (233)</option> <option value="266">PI10 (266)</option> <option value="265">PI9 (265)</option> <option value="256">PI0 (256)</option> <option value="271">PI15 (271)</option> <option value="267">PI11 (267)</option> <option value="268">PI12 (268)</option> <option value="258">PI2 (258)</option> <option value="76">PC12 (76)</option> <option value="272">PI16 (272)</option> <option value="260">PI4 (260)</option> <option value="259">PI3 (259)</option> <option value="custom">-- 自定义/环境变量 --</option> </select> </div> <div class="form-row" id="custom-pin-input-row" style="display: none;"> <label for="node-input-pin-custom"><i class="fa fa-pencil"></i> 自定义值</label> <input type="text" id="node-input-pin-custom" style="width: 70%;"> <input type="hidden" id="node-input-pinType-custom"> </div> </div> <div class="form-row" id="node-set-tick"> <label>&nbsp;</label> <input type="checkbox" id="node-input-enableInterrupt" style="display: inline-block; width: auto; vertical-align: top;"> <label for="node-input-enableInterrupt" style="width: 70%;">启用中断?</label> </div> <div class="form-row" id="node-set-edge" style="display: none;"> <label for="node-input-edge"><i class="fa fa-bullseye"></i> 触发边沿</label> <select id="node-input-edge" style="width: 70%;"> <option value="rising">上升沿</option> <option value="falling">下降沿</option> <option value="both">双边沿</option> </select> </div> <div class="form-row" id="node-set-debounce" style="display: none;"> <label for="node-input-debounce"><i class="fa fa-clock-o"></i> 去抖延迟 (ms)</label> <input type="text" id="node-input-debounce" placeholder="例如:50" style="width: 70%;"> </div> <div class="form-tips" id="node-tip-debounce" style="display: none;"> 去抖会在中断后增加一段死区时间,在此期间忽略新的中断 (0 = 禁用去抖)。 <br><br> <b>警告:</b> 并非所有引脚都支持硬件中断!请查阅设备文档。 </div> </script> <script type="text/html" data-help-name="opi_in"> <p>GPIO <b>输入</b> 节点,用于兼容多种设备(当前主要支持 <b>Orange Pi Zero 2W</b>)。</p> <p>请先从“设备型号”下拉列表中选择您的设备。根据所选设备,下方会显示引脚布局图和可用的 GPIO 引脚列表。</p> <p>选择要读取的 GPIO 引脚,或选择“-- 自定义/环境变量 --”来手动输入引脚号、环境变量名 (例如 <code>MY_PIN_VAR</code>) 或其他支持的类型。</p> <p>如果使用环境变量,请在子流程属性或 Node-RED 运行环境中定义它。</p> <p>用户有责任确保所选或输入的引脚号有效且不会干扰其他系统功能。</p> <p>可以通过向节点发送任何消息来轮询引脚当前状态。</p> <p><b>中断:</b> 您可以选择启用中断以在引脚状态改变时自动发送消息。选择触发边沿(上升沿、下降沿或双边沿)并可选择设置去抖时间(毫秒)以忽略快速的噪声信号。</p> <p><b>输出消息:</b><br> <code>msg.payload</code>: 包含引脚当前状态 (0 或 1)。<br> <code>msg.topic</code>: (可选) 可能包含引脚号或节点名称。<br> </p> <p><b>注意:</b> 并非所有引脚都支持中断。请参考您的设备文档。</p> </script> <script type="text/javascript"> RED.nodes.registerType('opi_in', { category: 'Orange Pi', // Or your desired category color: '#FFCC66', // Orange-ish color defaults: { name: {value: ""}, select: {value: "opz2w"}, // Default device pin: {value: "", required: true}, // Stores final pin value (number or string/var name) pinSource: {value: "list"}, // Source: 'list' or 'custom' pinType: {value: "num"}, // Type when pinSource is 'custom' ('num', 'str', 'env'...) enableInterrupt: {value: false}, // Enable interrupt? edge: {value: "rising"}, // Interrupt edge ('rising', 'falling', 'both') debounce: {value: "0", validate: RED.validators.number()} // Debounce time (ms), validate as number }, inputs: 1, // Allow input messages for polling outputs: 1, // Output pin state changes icon: "in.png", // Provide an icon file or use "fa-microchip" paletteLabel: "GPIO IN", label: function () { // Generate a dynamic label for the node let pinDisplay = this.pin; if (this.pinSource === 'custom') { switch (this.pinType) { case 'env': pinDisplay = '${' + this.pin + '}'; break; case 'flow': pinDisplay = '#' + this.pin; break; case 'global': pinDisplay = '#' + this.pin; break; } } const deviceLabel = this.select === 'opz2w' ? 'OPIZ2W' : (this.select || '设备'); const intLabel = this.enableInterrupt ? ` (${this.edge})` : ""; // Add edge info if interrupt enabled return this.name || (pinDisplay ? `${deviceLabel} GPIO ${pinDisplay}${intLabel}` : "GPIO IN"); }, oneditprepare: function () { const node = this; const resourcePath = "resources/@liball/node-red-contrib-opi-gpio"; // --------------------------------------------------------------------- // Get jQuery elements const $deviceSelect = $("#node-input-select"); const $imageContainer = $("#device-pinout-image-container"); const $imageElement = $("#device-pinout-image"); const $opz2wOptions = $("#opz2w-pin-options"); const $pinSelect = $("#node-input-pin-opz2w"); const $customPinRow = $("#custom-pin-input-row"); const $customPinInput = $("#node-input-pin-custom"); const $customPinType = $("#node-input-pinType-custom"); const $intEnableCheckbox = $("#node-input-enableInterrupt"); const $intEdgeRow = $("#node-set-edge"); const $intDebounceRow = $("#node-set-debounce"); const $intDebounceTip = $("#node-tip-debounce"); // Function to update UI based on selected device const updateUIForDevice = () => { const selectedDevice = $deviceSelect.val(); // Hide all device-specific options and image first $opz2wOptions.hide(); $imageContainer.hide(); $imageElement.attr("src", ""); // --- Handle Orange Pi Zero 2W --- if (selectedDevice === 'opz2w') { // 1. Show OPiZ2W options container $opz2wOptions.show(); // 2. Show Pinout Image const imageUrl = `${resourcePath}/gpio-zero2w.png`; // Assumes image is in node dir $imageElement.attr("src", imageUrl); $imageContainer.show(); // 3. Handle Pin Input Mode (List vs Custom) based on saved state if (node.pinSource === 'custom') { $pinSelect.val('custom'); $customPinRow.show(); if (!$customPinInput.data('typedInput')) { $customPinInput.typedInput({ default: node.pinType || 'num', types: ['num', 'str', 'env', 'flow', 'global'], typeField: $customPinType }); } $customPinInput.typedInput('value', node.pin); $customPinInput.typedInput('type', node.pinType || 'num'); } else { $pinSelect.val(node.pin); $customPinRow.hide(); } } // --- Handle Other Devices --- // else if (selectedDevice === 'other_device') { ... } }; // Function to show/hide interrupt settings const setInterruptVisibility = function () { if ($intEnableCheckbox.is(":checked")) { $intEdgeRow.show(); $intDebounceRow.show(); $intDebounceTip.show(); } else { $intEdgeRow.hide(); $intDebounceRow.hide(); $intDebounceTip.hide(); } }; // --- Event Handlers --- // 1. Device selection changes $deviceSelect.change(() => { updateUIForDevice(); }); // 2. Pin dropdown selection changes $pinSelect.change(function () { const selectedValue = $(this).val(); if (selectedValue === 'custom') { $customPinRow.show(); if (!$customPinInput.data('typedInput')) { $customPinInput.typedInput({ default: 'num', types: ['num', 'str', 'env', 'flow', 'global'], typeField: $customPinType }); } } else { $customPinRow.hide(); // Optionally update name based on selection // const selectedText = $(this).find('option:selected').text(); // $("#node-input-name").val(selectedText); } }); // 3. Interrupt checkbox changes $intEnableCheckbox.change(setInterruptVisibility); // --- Initialization on Dialog Open --- // Set initial values from node properties $deviceSelect.val(node.select || 'opz2w'); $intEnableCheckbox.prop('checked', node.enableInterrupt); // Set interrupt checkbox $("#node-input-edge").val(node.edge || 'rising'); // Set edge dropdown $("#node-input-debounce").val(node.debounce || '0'); // Set debounce input // Update the device/pin/image UI based on loaded device and pin source updateUIForDevice(); // Update visibility of interrupt settings setInterruptVisibility(); }, oneditsave: function () { const node = this; // Save device selection node.select = $("#node-input-select").val(); // Determine pin source and save pin/pinType accordingly const pinSelectionMode = $("#node-input-pin-opz2w").val(); // Check the dropdown if (pinSelectionMode === 'custom') { node.pinSource = 'custom'; node.pin = $("#node-input-pin-custom").typedInput('value'); node.pinType = $("#node-input-pin-custom").typedInput('type'); } else { node.pinSource = 'list'; node.pin = pinSelectionMode; // Get value directly from dropdown node.pinType = 'num'; // Pins from list are always numbers } // Note: enableInterrupt, edge, debounce defaults are automatically saved by Node-RED } }); </script>