技术问询:如何用JS连接Flipper Zero并发送文件至/int/或重启设备
实现Flipper Zero的文件传输与重启功能
首先你需要完善串口通信的读写逻辑——仅写入命令而不读取响应会导致缓冲区阻塞,同时Flipper的CLI命令必须以\r\n结尾才能触发执行。
1. 完善连接逻辑,添加响应读取器
修改你的连接函数,同时获取串口的Reader用于读取设备反馈:
let flipper; let writer; let reader; let connected = false; const connect = async () => { try { flipper = await navigator.serial.requestPort({ filters: [{ usbVendorId: 0x0483 }] }); await flipper.open({ baudRate: 9600 }); // 初始化读写器 writer = flipper.writable.getWriter(); reader = flipper.readable.getReader(); connected = true; console.log("Flipper Zero 已连接"); // 读取设备初始欢迎信息,清空缓冲区 await readResponse(); } catch (error) { console.error('打开串口失败:', error); } }; // 读取设备响应的通用函数,直到检测到CLI提示符`>` const readResponse = async () => { if (!reader) return ''; let response = ''; try { while (true) { const { value, done } = await reader.read(); if (done) break; response += new TextDecoder().decode(value); if (response.includes('> ')) break; } } catch (error) { console.error('读取响应失败:', error); } return response; };
2. 发送CLI命令的通用函数
基于Flipper的串口CLI协议,封装命令发送逻辑:
const sendCommand = async (command) => { if (!connected || !writer) return; try { const encoder = new TextEncoder(); // 命令末尾必须加\r\n触发执行 await writer.write(encoder.encode(`${command}\r\n`)); const response = await readResponse(); console.log(`命令执行反馈: ${response}`); return response; } catch (error) { console.error('发送命令失败:', error); } };
3. 实现设备重启功能
Flipper的CLI重启命令为reboot,直接调用通用命令函数即可:
const rebootFlipper = async () => { await sendCommand('reboot'); // 重启后设备断开连接,重置状态 connected = false; writer.releaseLock(); reader.releaseLock(); await flipper.close(); };
4. 实现文件发送到/int/目录
4.1 文本文件传输(适合小文件)
利用file writeCLI命令直接发送文本内容,注意路径格式为/int/[文件名]:
const sendTextFileToInt = async (file, filename) => { if (!connected || !file) return; try { const fileContent = await file.text(); // 内容带引号避免空格/特殊字符解析错误 const command = `file write /int/${filename} "${fileContent}"`; await sendCommand(command); console.log(`文件 ${filename} 已发送至/int/目录`); } catch (error) { console.error('发送文本文件失败:', error); } };
4.2 二进制文件传输(适合大文件/非文本文件)
对于二进制文件(如固件、NFC数据),推荐使用Flipper支持的XMODEM协议,避免CLI命令长度限制:
const sendBinaryFileToInt = async (file, filename) => { if (!connected || !file) return; try { // 触发XMODEM接收模式 await sendCommand(`file write /int/${filename} xmodem`); // 等待设备准备就绪 await new Promise(resolve => setTimeout(resolve, 500)); const fileBuffer = await file.arrayBuffer(); const fileBytes = new Uint8Array(fileBuffer); const PACKET_SIZE = 128; // XMODEM标准数据包大小 let packetNumber = 1; let offset = 0; while (offset < fileBytes.length) { // 构造XMODEM数据包:SOH(0x02) + 包号 + 反包号 + 数据 + 校验和 const packet = new Uint8Array(PACKET_SIZE + 4); packet[0] = 0x02; packet[1] = packetNumber; packet[2] = 0xFF - packetNumber; // 填充数据块 const chunk = fileBytes.slice(offset, offset + PACKET_SIZE); packet.set(chunk, 3); // 计算校验和 let checksum = 0; for (let i = 3; i < 3 + chunk.length; i++) checksum += packet[i]; packet[3 + PACKET_SIZE] = checksum & 0xFF; // 发送数据包并等待ACK(0x06)响应 await writer.write(packet); const { value } = await reader.read(); if (value[0] !== 0x06) throw new Error('数据包发送失败,未收到ACK'); packetNumber++; offset += PACKET_SIZE; } // 发送EOF(0x04)结束传输 await writer.write(new Uint8Array([0x04])); console.log(`二进制文件 ${filename} 已发送至/int/目录`); } catch (error) { console.error('发送二进制文件失败:', error); } };
注意事项
- 确保串口波特率与Flipper默认值(9600)一致
/int/为内部存储目录,部分文件类型需符合Flipper的格式要求- 大文件优先使用XMODEM协议,避免CLI命令长度限制
- 执行命令后必须读取响应,防止串口缓冲区阻塞
内容的提问来源于stack exchange,提问作者roooot
相关产品推荐
相关产品推荐

