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

Electron.js环境下Arduino控制的本地音频录制方案技术问询

我之前做过类似的硬件+JS录音项目,完全适配你的需求——不用用户碰隐藏的Windows笔记本,靠Arduino按钮控制录音启停,本地存储单独文件,全程用JS生态实现。下面是完整的落地方案:

核心思路

用Electron打包JS代码成Windows桌面应用,它既能调用Node.js的硬件/文件API,又能使用浏览器的MediaRecorder录音API。Arduino通过串口给Electron发送按钮指令,Electron监听指令触发录音,每次录音生成带时间戳的独立文件,全程后台运行,用户完全感知不到笔记本的存在。

1. Arduino按钮控制代码

先搞定Arduino端,让按钮按下时发送录音切换指令(加防抖避免误触):

const int buttonPin = 2;
int lastButtonState = HIGH;
int currentButtonState;
unsigned long lastDebounceTime = 0;
unsigned long debounceDelay = 50;

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  Serial.begin(9600);
}

void loop() {
  currentButtonState = digitalRead(buttonPin);
  
  if (currentButtonState != lastButtonState) {
    lastDebounceTime = millis();
  }

  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (currentButtonState == LOW && lastButtonState == HIGH) {
      // 按钮按下,发送切换指令(第一次按启动,第二次按停止)
      Serial.println("TOGGLE_REC");
    }
    lastButtonState = currentButtonState;
  }
}
2. Electron应用核心实现(JS)

初始化项目

先创建Electron项目,安装依赖:

npm init -y
npm install electron serialport @serialport/parser-readline

主进程代码(main.js)

负责串口监听、窗口隐藏、开机自启:

const { app, BrowserWindow, ipcMain } = require('electron');
const SerialPort = require('serialport');
const Readline = require('@serialport/parser-readline');
const path = require('path');

let mainWindow;
let isRecording = false;

function createWindow() {
  mainWindow = new BrowserWindow({
    width: 800,
    height: 600,
    show: false, // 隐藏窗口,用户完全看不到
    webPreferences: {
      nodeIntegration: true,
      contextIsolation: false
    }
  });

  mainWindow.loadFile('index.html');

  // 自动检测Arduino串口(也可以手动指定端口,比如COM3)
  SerialPort.list().then(ports => {
    // 匹配Arduino的vendorId,不同型号可能略有差异,可自行调整
    const arduinoPort = ports.find(port => port.vendorId === '2341' || port.vendorId === '2A03');
    if (arduinoPort) {
      const port = new SerialPort(arduinoPort.path, { baudRate: 9600 });
      const parser = port.pipe(new Readline({ delimiter: '\r\n' }));
      
      parser.on('data', (data) => {
        if (data.trim() === 'TOGGLE_REC') {
          isRecording = !isRecording;
          // 给渲染进程发消息,切换录音状态
          mainWindow.webContents.send('toggle-recording', isRecording);
        }
      });
    } else {
      console.error('未找到Arduino设备,请检查USB连接');
    }
  });

  // 设置开机自启,且后台隐藏运行
  app.setLoginItemSettings({
    openAtLogin: true,
    openAsHidden: true
  });
}

app.whenReady().then(createWindow);

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') app.quit();
});

渲染进程代码(index.html + 内嵌JS)

负责录音和本地文件存储:

<!DOCTYPE html>
<html>
<body>
  <script>
    const { ipcRenderer } = require('electron');
    const fs = require('fs');
    const path = require('path');
    const os = require('os');

    let mediaRecorder;
    let audioChunks = [];
    // 录音存储目录:用户文档下的RecordingFiles文件夹
    const saveDir = path.join(os.homedir(), 'Documents', 'ExperienceRecordings');
    
    // 提前创建存储目录,避免保存失败
    if (!fs.existsSync(saveDir)) {
      fs.mkdirSync(saveDir, { recursive: true });
    }

    // 获取麦克风权限并初始化录音流
    async function initAudioStream() {
      try {
        const stream = await navigator.mediaDevices.getUserMedia({ 
          audio: { sampleRate: 44100, channelCount: 1 } // 配置单声道、标准采样率
        });
        mediaRecorder = new MediaRecorder(stream);
        
        mediaRecorder.ondataavailable = (event) => {
          if (event.data.size > 0) {
            audioChunks.push(event.data);
          }
        };

        mediaRecorder.onstop = async () => {
          const audioBlob = new Blob(audioChunks, { type: 'audio/wav' });
          // 用时间戳命名,确保每个录音文件唯一
          const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
          const fileName = `recording-${timestamp}.wav`;
          const filePath = path.join(saveDir, fileName);
          
          // 把Blob转成Buffer保存到本地
          fs.writeFile(filePath, Buffer.from(await audioBlob.arrayBuffer()), (err) => {
            if (err) console.error('保存录音失败:', err);
            else console.log(`录音已保存: ${filePath}`);
          });
          
          audioChunks = []; // 重置缓存,准备下一次录音
        };
      } catch (err) {
        console.error('麦克风权限获取失败:', err);
      }
    }

    // 初始化音频流
    initAudioStream();

    // 监听主进程的录音切换指令
    ipcRenderer.on('toggle-recording', (event, start) => {
      if (start && mediaRecorder) {
        mediaRecorder.start();
        console.log('录音开始');
      } else if (mediaRecorder && mediaRecorder.state === 'recording') {
        mediaRecorder.stop();
        console.log('录音停止');
      }
    });
  </script>
</body>
</html>
3. 部署与测试
  1. 打包应用:安装electron-builder,在package.json中添加build配置,打包成Windows exe安装包,安装到目标笔记本上。
  2. 串口测试:确保Arduino通过USB连接笔记本,串口驱动正常,应用能识别到设备。
  3. 功能验证:按下Arduino按钮,检查文档/ExperienceRecordings目录下是否生成带时间戳的wav文件。
  4. 后台运行:重启笔记本后,应用会自动后台启动,用户完全无需操作笔记本。
关键注意事项
  • 串口权限:Windows下可能需要管理员权限访问串口,打包时可配置应用请求管理员权限。
  • 麦克风权限:第一次运行应用会弹出权限请求,允许后后续启动不会再提示。
  • 文件兼容性:用wav格式保存,确保录音文件能在任意设备上播放。

内容的提问来源于stack exchange,提问作者user9685408

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 08:05:53