You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

技术问询:如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 02:17:05