---
title: "[會議記錄]20250702-暑期例行性會議"
description:
image:
tags:
- 會議記錄
- 社課
- 社團博覽會
- 電資創客營
---
# 暑期例行性會議
:::warning
## 會議資訊
日期:2025/07/02
時間:13:00~:00
地點:社辦、Discord線上
:::
列席人員:
- ...
出席人員:
- 陳柏屹
- 游凱崴
- 羅崧瑋
- 陳威翰
- 方健宇
- 蔡詠竣
- 黃郁寧
- 張乃文
- 張登評
缺席人員:
- ...
## 📚 會前須知
:::info
**文件**
- [教學組內會議](https://hackmd.io/@ttussc/rJw2jAgBxe)
- [教育優先區會議-第零籌](https://hackmd.io/@ttussc/rkBa2RO4xl)
:::
:::spoiler 課表
上學期
- 9/11 期初大會
- 9/18 開源 + 社課規劃
- 9/25 Markdown
- 10/2 Shell + Vim
- 10/9 程式語言
- 10/16 程式語言
- 10/23 讀書會
- 10/30 期中考周
- 11/6 吃東西(社遊)
- 11/13 Discord Bot
- 11/20 Linux
- 11/27 資安
- 12/4 資安 (CTF) or ~~Bad USB~~
- 12/11 期末大會
- 12/18 讀書會
- 12/25 期末考週
下學期
- 2/26 期初大會
- 3/5 小專題分組與說明
- 3/12 Git
- 3/19 Git
- 3/26 前端
- 4/2 前端
- 4/9 讀書會
- 4/16 期中考週
- 4/23 社遊
- 4/30 後端
- 5/7 Docker
- 5/14 ?
- 5/21 ?
- 5/28 期末大會 + 小專題發表會
- 6/4 讀書會
- 6/11 期末考週
:::
### 本次預計進度
- ...
## 📣 議程
- 下學期社課
- 攝影營隊、電資創客營確認進度
- 教育優先區營隊與另外兩校線上會議時間
- 臨時動議
- 散會
## ℹ️ 報告事項
> 從這裡開始
## 📝 會議紀錄
### 社課規劃
表定 9:00,但有可能只上到 8:30 ~~早點下課~~
上學期
- 9/11 期初大會 社長
- 9/18 開源+社課規劃 社長
- 9/25 Markdown 詠竣
- 10/2 Shell+Vim 詠竣
- 10/9 Python 乃文
- 10/16 Python 乃文
- 10/23 讀書會
- 10/30 期中考周
- 11/6 吃東西(社遊)
- 11/13 Discord Bot 詠竣
- 11/20 Linux 詠竣
- 11/27 資安 崧瑋
- 12/4 資安 (CTF) or ~~Bad USB~~ 崧瑋
- 12/11 期末大會
- 12/18 讀書會
- 12/25 期末考週
下學期
- 2/26 期初大會
- 3/5 小專題分組與說明
- 3/12 Git
- 3/19 Git
- 3/26 前端
- 4/2 前端
- 4/9 讀書會
- 4/16 期中考週
- 4/23 社遊
- 4/30 後端
- 5/7 Docker
- 5/14 專題討論(暫定) or 社員意願調查
- 5/21 專題討論(暫定) or 社員意願調查
- 5/28 期末大會+小專題發表會
- 6/4 讀書會
- 6/11 期末考週
==8/25== 前講師繳企劃書給黃郁寧
出去玩:六度空間玩槍戰(平日、2小時:390 元/人)
> :::info
> 攝影營隊
> :::
> 兒童心理學 7/4 18:00~20:00 102教室
> 7/12、13 排桌椅
> 原本的便當一直遲到,看要不要換築本屋訂90塊便當
> 講師:大倫跟Leo
> 凱崴要抓細流給工作人員
> :::info
> 電資創客營
> :::
> 204不能借、200要問電機系主任,其他都可以
> 原價2500、早鳥2000、低收700 (成本1700)
> 講師:還在找
> 7/7 (一) 文案:芋泥
> 發宣傳文 (ig、fb):芋泥
> esp32之前的bug:
> ```
> E (9) esp_clock_output: esp_clock_output_start(207): Selected io is already mapped by another signal
> E (9) i2s(legacy): i25_check_set_mclk (1878): mclk configure failed
> E (13) i2s(legacy): i2s_set_pin (1947): mclk config failed
解決了,但是在傳輸上還是會出現
Gemini連結:https://g.co/gemini/share/503b7f2efa7e
::: spoiler **這是現在整合用的程式:**
```
#include <WiFi.h>
#include <esp_now.h>
#include <driver/i2s.h>
// ====================================== 腳位設定 ======================================
// INMP441 麥克風 (I2S0 - Master)
#define I2S_MIC_WS 25 // 麥克風的 WS 腳位,同時接到喇叭的 LRC
#define I2S_MIC_BCLK 26 // 麥克風的 BCLK 腳位,同時接到喇叭的 BCLK
#define I2S_MIC_SD 32 // 麥克風的數據輸出 (SD)
#define I2S_MIC_MCLK 4 // 麥克風 Master 模式下 MCLK 輸出腳位 (可選,若晶片需要)
// MAX98357A 喇叭 (I2S1 - Slave)
// 注意:喇叭的 BCLK 和 LRC 將會從麥克風的 BCLK 和 WS 腳位接收訊號!
#define I2S_SPK_LRC 13 // 喇叭的 LRC 腳位 (接到麥克風的 WS)
#define I2S_SPK_BCLK 14 // 喇叭的 BCLK 腳位 (接到麥克風的 BCLK)
#define I2S_SPK_DIN 12 // 喇叭的數據輸入 (DIN)
// Slave 模式下不需要定義 I2S_SPK_MCLK,因為不產生 MCLK
#define BUTTON_PIN 33 // 按鈕腳位
#define SAMPLE_RATE 20000 // 取樣率
#define BUFFER_LEN 64 // DMA 緩衝區長度(每個緩衝區的樣本數)
#define NOISE_THRESHOLD 60 // 靜音閾值
#define FILTER_TAP_COUNT 2 // 移動平均濾波器抽頭數
#define I2S_MIC_PORT I2S_NUM_0 // 麥克風使用 I2S 0
#define I2S_SPK_PORT I2S_NUM_1 // 喇叭使用 I2S 1
//uint8_t peerMac[] = {0xA8, 0x48, 0xFA, 0x0B, 0x89, 0x3C}; // ⚠️ 請替換為對方 ESP32 的 MAC 地址
uint8_t peerMac[] = {0xA4, 0xE5, 0x7C, 0xD3, 0xB4, 0x5C};
// ====================================== I2S 初始化函數 ======================================
// 麥克風 I2S 初始化 (Master 模式)
void setupMicI2S() {
Serial.println("初始化麥克風 I2S (Master 模式)...");
const i2s_config_t mic_config = {
.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX), // Master 模式接收
.sample_rate = SAMPLE_RATE,
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
.communication_format = I2S_COMM_FORMAT_I2S_MSB,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 8,
.dma_buf_len = BUFFER_LEN,
.use_apll = true, // Master 模式通常使用 APLL 產生精確時脈
.tx_desc_auto_clear = false,
.fixed_mclk = I2S_MIC_MCLK // Master 在此腳位輸出 MCLK (例如 GPIO4)
};
const i2s_pin_config_t mic_pins = {
.bck_io_num = I2S_MIC_BCLK, // 麥克風 BCLK 輸出
.ws_io_num = I2S_MIC_WS, // 麥克風 WS/LRC 輸出
.data_out_num = I2S_PIN_NO_CHANGE, // 麥克風是 RX,無數據輸出
.data_in_num = I2S_MIC_SD, // 麥克風數據輸入
// .mclk_io_num 已經被移除,因為 I2S_PIN_CONFIG_T 結構體中不再有此成員
};
esp_err_t err = i2s_driver_install(I2S_MIC_PORT, &mic_config, 0, NULL);
if (err != ESP_OK) {
Serial.printf("❌ 麥克風 I2S 驅動安裝失敗: %d\n", err);
while(true);
}
err = i2s_set_pin(I2S_MIC_PORT, &mic_pins);
if (err != ESP_OK) {
Serial.printf("❌ 麥克風 I2S 腳位設定失敗: %d\n", err);
while(true);
}
i2s_zero_dma_buffer(I2S_MIC_PORT);
Serial.println("✅ 麥克風 I2S 初始化完成 (Master 模式)");
}
// 喇叭 I2S 初始化 (Slave 模式)
void setupSpkI2S() {
Serial.println("初始化喇叭 I2S (Slave 模式)...");
const i2s_config_t spk_config = {
.mode = (i2s_mode_t)(I2S_MODE_SLAVE | I2S_MODE_TX), // Slave 模式傳送
.sample_rate = SAMPLE_RATE, // 喇叭的取樣率需與 Master (麥克風) 相同
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
.channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
.communication_format = I2S_COMM_FORMAT_I2S_MSB,
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
.dma_buf_count = 8,
.dma_buf_len = BUFFER_LEN,
.use_apll = false, // Slave 模式不需要 APLL (因為不產生時脈)
.tx_desc_auto_clear = true,
.fixed_mclk = I2S_PIN_NO_CHANGE // Slave 模式不輸出 MCLK
};
const i2s_pin_config_t spk_pins = {
.bck_io_num = I2S_SPK_BCLK, // 喇叭 BCLK 輸入 (將連接到麥克風的 BCLK 輸出)
.ws_io_num = I2S_SPK_LRC, // 喇叭 WS/LRC 輸入 (將連接到麥克風的 WS 輸出)
.data_out_num = I2S_SPK_DIN, // 喇叭數據輸出
.data_in_num = I2S_PIN_NO_CHANGE, // 喇叭是 TX,無數據輸入
// .mclk_io_num 已經被移除,因為 I2S_PIN_CONFIG_T 結構體中不再有此成員
};
esp_err_t err = i2s_driver_install(I2S_SPK_PORT, &spk_config, 0, NULL);
if (err != ESP_OK) {
Serial.printf("❌ 喇叭 I2S 驅動安裝失敗: %d\n", err);
while(true);
}
err = i2s_set_pin(I2S_SPK_PORT, &spk_pins);
if (err != ESP_OK) {
Serial.printf("❌ 喇叭 I2S 腳位設定失敗: %d\n", err);
while(true);
}
i2s_zero_dma_buffer(I2S_SPK_PORT);
Serial.println("✅ 喇叭 I2S 初始化完成 (Slave 模式)");
}
// ====================================== ESP-NOW 回調函數 ======================================
// 接收音訊資料後播放
void onDataReceive(const esp_now_recv_info_t *recv_info, const uint8_t *incomingData, int len) {
size_t bytesWritten;
int16_t *samples = (int16_t *)incomingData; // 將 incomingData 轉換為 int16_t 指針
int sampleCount = len / sizeof(int16_t);
// 計算音量 (平均絕對值) - 用於閾值判斷和除錯
uint32_t current_sum_abs = 0;
for (int i = 0; i < sampleCount; i++) {
current_sum_abs += abs(samples[i]);
}
int avgVolume = current_sum_abs / sampleCount;
// ==== 雜訊過濾處理 ====
// 1. 閾值降噪 (Noise Gating)
if (avgVolume < NOISE_THRESHOLD) {
// 如果平均音量低於閾值,則將所有樣本設為 0 (靜音)
for (int i = 0; i < sampleCount; i++) {
samples[i] = 0;
}
// Serial.println("📥 接收音訊:靜音中 (低於閾值)"); // 太多訊息時可註解
} else {
// 2. 移動平均濾波器
for (int i = 0; i < sampleCount; i++) {
long sum_filter = 0;
int count_filter = 0;
for (int j = 0; j < FILTER_TAP_COUNT; j++) {
if (i - j >= 0) { // 確保不越界
sum_filter += samples[i - j];
count_filter++;
}
}
samples[i] = (int16_t)(sum_filter / count_filter);
}
// Serial.printf("📥 接收音訊:%d bytes,音量:%d\n", len, avgVolume); // 太多訊息時可註解
}
// 將處理後的音訊資料寫入 I2S 輸出
i2s_write(I2S_SPK_PORT, samples, len, &bytesWritten, portMAX_DELAY);
}
// ====================================== 任務函數 ======================================
// 麥克風讀取並傳送任務
void micSendTask(void *pvParameters) {
int16_t buffer[BUFFER_LEN];
size_t bytesRead;
Serial.println("📤 麥克風傳送任務啟動");
while (true) {
// 從麥克風讀取音訊
i2s_read(I2S_MIC_PORT, buffer, sizeof(buffer), &bytesRead, portMAX_DELAY);
// 計算音量
uint32_t sum = 0;
for (int i = 0; i < BUFFER_LEN; i++) sum += abs(buffer[i]);
int avg = sum / BUFFER_LEN;
// 如果按鈕按下 (LOW) 就傳送
if (digitalRead(BUTTON_PIN) == LOW) {
esp_err_t result = esp_now_send(peerMac, (uint8_t *)buffer, sizeof(buffer));
if (result == ESP_OK) {
Serial.printf("📤 傳送中,音量:%d\n", avg);
} else {
Serial.println("⚠️ 傳送失敗");
}
} else {
// Serial.println("🎤 靜待傳送 (按鈕未按下)"); // 太多訊息時可註解
}
vTaskDelay(pdMS_TO_TICKS(4)); // 短暫延遲
}
}
// 接收任務 (僅為 FreeRTOS 任務結構,實際處理在 onDataReceive 回調中)
void recvTask(void *pvParameters) {
Serial.println("🎧 接收任務啟動 (等待 ESP-NOW 數據)");
while (true) {
vTaskDelay(pdMS_TO_TICKS(10)); // 緩衝 CPU
}
}
// ====================================== Setup 和 Loop ======================================
void setup() {
Serial.begin(115200);
delay(100); // 給序列埠一點時間啟動
Serial.println("=====================================");
Serial.println(" ESP32 雙向對講機啟動中... ");
Serial.println("=====================================");
pinMode(BUTTON_PIN, INPUT_PULLUP); // 設定按鈕腳位為輸入上拉
// 初始化 I2S 驅動
setupMicI2S(); // 麥克風 (Master)
setupSpkI2S(); // 喇叭 (Slave)
// 初始化 WiFi 和 ESP-NOW
WiFi.mode(WIFI_STA); // 設定 WiFi 模式為站點模式
delay(100); // 等待 WiFi 模組初始化
// 打印本機 MAC 地址
Serial.print("本機 MAC 地址: ");
Serial.println(WiFi.macAddress());
if (esp_now_init() != ESP_OK) {
Serial.println("❌ ESP-NOW 初始化失敗!");
while (true); // 初始化失敗則停在此處
}
Serial.println("✅ ESP-NOW 初始化成功");
// 添加 ESP-NOW 對等端
esp_now_peer_info_t peerInfo = {};
memcpy(peerInfo.peer_addr, peerMac, 6);
peerInfo.channel = 0; // 頻道必須與對方匹配
peerInfo.encrypt = false; // 不加密
if (!esp_now_is_peer_exist(peerMac)) { // 檢查對等端是否已存在
if (esp_now_add_peer(&peerInfo) != ESP_OK) {
Serial.println("❌ 加入 ESP-NOW 對等端失敗!");
while (true); // 加入失敗則停在此處
}
Serial.println("✅ ESP-NOW 對等端添加成功");
} else {
Serial.println("✅ ESP-NOW 對等端已存在");
}
// 註冊 ESP-NOW 接收回調函數
esp_now_register_recv_cb(onDataReceive);
Serial.println("✅ ESP-NOW 接收回調函數註冊成功");
// 創建 FreeRTOS 任務
// 麥克風傳送任務運行在 Core 0
xTaskCreatePinnedToCore(micSendTask, "MicSendTask", 4096, NULL, 1, NULL, 0);
// 接收任務運行在 Core 1 (主要處理 ESP-NOW 回調,保持獨立)
xTaskCreatePinnedToCore(recvTask, "RecvTask", 4096, NULL, 1, NULL, 1);
Serial.println("=====================================");
Serial.println(" ✅ 雙向對講機啟動完成! ");
Serial.println("=====================================");
Serial.println("請確認 I2S 腳位接線,特別是 BCLK 和 WS/LRC 的共享!");
}
void loop() {
// loop 函數可以保持為空,所有邏輯都在 FreeRTOS 任務中處理
vTaskDelay(pdMS_TO_TICKS(1000)); // 防止看門狗定時器超時
}
```
:::
> :::info
> 社團博覽會
> :::
> **設計:**
> - 限動、貼文 -> Jenny
> - 海報 -> 昭妤
> - 更新網站 -> 詠竣
> - 桌巾 -> 小方
> - 關東旗(大/小) -> 小方
> - 宣傳簡報更新(平板) -> 柏屹
> - 各系社團茶會宣傳 -> 柏屹、凱崴
> - 新生輔導宣傳 -> 各系較熟的去
> - 宣傳單沿用 -> ?
> - 檢查QRcord(名片、宣傳單)
> :::info
> 討論事項
> :::
>
> 討論過程紀錄...
:::danger
15:11 會議結束~
:::
:::danger
下次會議時間:7/16
檢查事項:
- 關東旗、桌巾、傳單、海報、~~社服~~的報價
- 電資創客營
- 報名、講師
:::
## 🔑 會議總結
> 從這裡開始