ESP32_0812_NTU_TEM.ino
/*
* ESP32 (translated) ((translated) - (translated) + (translated))
*/
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Wire.h>
#include <SSD1306Wire.h>
#include <RTClib.h>
#include <BH1750.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <SPI.h>
#include <SD.h>
#include <time.h>
// Pin definitions
#define TEMP_SENSOR_PIN 4
#define TURBIDITY_ANALOG_PIN 34 // 🔥 (translated)pin (ADC1_CH6)
#define I2C_SDA 21
#define I2C_SCL 22
#define SD_CS_PIN 5
#define SD_MOSI_PIN 23
#define SD_MISO_PIN 19
#define SD_SCK_PIN 18
// WiFi setup
const char* ssid = "TP-Link_6D5A";
const char* password = "********";
// IFTTT setup
const String iftttEvent = "water_quality_data";
const String iftttKey = "****************";
const String iftttUrl = "http://maker.ifttt.com/trigger/" + iftttEvent + "/with/key/" + iftttKey;
// Interval settings
const long sdLogInterval = 180000; // 3 minutes
const long uploadInterval = 180000; // 3 minutes
const long sensorInterval = 2000;
const long displayInterval = 3000;
const long tempCheckInterval = 30000;
const long ntpSyncInterval = 3600000; // 1 hourssync(translated)NTP
unsigned long previousSDLogTime = 0;
unsigned long previousUploadTime = 0;
unsigned long previousSensorTime = 0;
unsigned long previousDisplayTime = 0;
unsigned long lastTempCheckTime = 0;
unsigned long lastNTPSyncTime = 0;
// 🔥 Time management variables
unsigned long bootTime = 0; // Boot timestamp
bool hasValidBootTime = false; // (translated)
bool ntpSynced = false; // NTP(translated)sync
// 🔥 (translated)sensorvariable
float latestTurbidityVoltage = 0.0; // voltage(translated)
float latestTurbidityNTU = 0.0; // NTU(translated)
int turbidityRawValue = 0; // ADC(translated)
const int turbidityReadings = 10; // averageread(translated)
const float voltageReference = 3.3; // ESP32(translated)voltage
// initialize(translated)
SSD1306Wire display(0x3C, I2C_SDA, I2C_SCL);
OneWire oneWire(TEMP_SENSOR_PIN);
DallasTemperature tempSensors(&oneWire);
RTC_DS3231 rtc;
BH1750 lightMeter;
// status(translated)
bool rtcOK = false;
bool tempSensorOK = false;
bool turbidityOK = false;
bool lightSensorOK = false;
bool oledOK = false;
bool wifiOK = false;
bool sdCardOK = false;
// sensor(translated)
float latestTemperature = -999;
float latestLightLevel = 0;
String latestDateString = "";
String latestTimeString = "";
String latestDateTime = "";
int displayPage = 0;
int uploadCounter = 0;
int sdLogCounter = 0;
char logFileName[20] = "";
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
// 🔥 (translated)RTCstatus
void checkRTCStatus() {
if (!rtc.begin()) {
Serial.println("RTC 模組未找到");
rtcOK = false;
return;
}
if (rtc.lostPower()) {
Serial.println("RTC 失去電源,需要重新設定時間");
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
rtcOK = true;
} else {
rtcOK = true;
// record(translated)
bootTime = rtc.now().unixtime();
hasValidBootTime = true;
Serial.println("RTC 時間有效,已記錄開機時間");
}
}
// 🔥 NTP(translated)sync
void syncTimeWithNTP() {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi未連接,無法同步NTP");
return;
}
Serial.println("開始NTP時間同步...");
// settingsNTPserver ((translated) GMT+8)
configTime(8 * 3600, 0, "pool.ntp.org", "time.nist.gov", "time.google.com");
struct tm timeinfo;
int retries = 0;
// (translated)NTPsync, (translated)10(translated)
while (!getLocalTime(&timeinfo) && retries < 10) {
delay(1000);
retries++;
Serial.print(".");
}
if (retries < 10) {
// NTPsyncsuccess
if (rtcOK) {
rtc.adjust(DateTime(timeinfo.tm_year + 1900,
timeinfo.tm_mon + 1,
timeinfo.tm_mday,
timeinfo.tm_hour,
timeinfo.tm_min,
timeinfo.tm_sec));
Serial.println("\nRTC時間已與NTP同步");
}
// update(translated)
bootTime = mktime(&timeinfo) - (millis() / 1000);
hasValidBootTime = true;
ntpSynced = true;
Serial.printf("同步時間: %04d/%02d/%02d %02d:%02d:%02d\n",
timeinfo.tm_year + 1900, timeinfo.tm_mon + 1, timeinfo.tm_mday,
timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec);
} else {
Serial.println("\nNTP同步失敗");
}
}
// 🔥 (translated)read(translated) ((translated)NTU(translated)voltage)
void readTurbidityAnalog() {
long sum = 0;
// (translated)read(translated)average(translated), (translated)
for (int i = 0; i < turbidityReadings; i++) {
sum += analogRead(TURBIDITY_ANALOG_PIN);
delay(10);
}
turbidityRawValue = sum / turbidityReadings;
// (translated)voltage(translated) (ESP32 ADC: 0-4095 (translated) 0-3.3V)
latestTurbidityVoltage = (turbidityRawValue * voltageReference) / 4095.0;
// 🔥 voltage(translated)NTU(translated) ((translated)sensor(translated))
// (translated)convert(translated), (translated)sensor(translated)
if (latestTurbidityVoltage > 2.5) {
latestTurbidityNTU = 0.0; // (translated)
} else if (latestTurbidityVoltage > 2.0) {
latestTurbidityNTU = (2.5 - latestTurbidityVoltage) * 100; // 0-50 NTU
} else if (latestTurbidityVoltage > 1.5) {
latestTurbidityNTU = 50 + (2.0 - latestTurbidityVoltage) * 200; // 50-150 NTU
} else if (latestTurbidityVoltage > 1.0) {
latestTurbidityNTU = 150 + (1.5 - latestTurbidityVoltage) * 400; // 150-350 NTU
} else {
latestTurbidityNTU = 350 + (1.0 - latestTurbidityVoltage) * 650; // 350-1000 NTU
}
// (translated)NTU(translated)range(translated)
if (latestTurbidityNTU < 0) latestTurbidityNTU = 0;
if (latestTurbidityNTU > 1000) latestTurbidityNTU = 1000;
}
// SD(translated)initialize
bool initSDCard() {
SPI.end();
delay(100);
SPI.begin(SD_SCK_PIN, SD_MISO_PIN, SD_MOSI_PIN);
SPI.setFrequency(1000000);
delay(200);
pinMode(SD_CS_PIN, OUTPUT);
digitalWrite(SD_CS_PIN, HIGH);
delay(100);
for (int retry = 0; retry < 3; retry++) {
if (SD.begin(SD_CS_PIN, SPI, 1000000)) {
// (translated)log(translated)
if (rtcOK) {
DateTime dt = rtc.now();
sprintf(logFileName, "/%04u%02u%02u.csv", dt.year(), dt.month(), dt.day());
} else {
sprintf(logFileName, "/data.csv");
}
if (!SD.exists(logFileName)) {
File dataFile = SD.open(logFileName, FILE_WRITE);
if (dataFile) {
// 🔥 (translated)SD(translated)recordformat - (translated)record(translated)NTU(translated)voltage
dataFile.println("Date(Time),Turbidity(NTU),Turbidity(Voltage)");
dataFile.close();
}
}
return true;
}
delay(500);
}
return false;
}
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("=== ESP32 水質監測系統 (類比濁度版) ===");
// 🔥 setup(translated)sensorpin
pinMode(TURBIDITY_ANALOG_PIN, INPUT);
// initializeI2C
Wire.begin(I2C_SDA, I2C_SCL);
Wire.setClock(100000);
delay(100);
// initializeOLED
if (display.init()) {
display.flipScreenVertically();
display.setFont(ArialMT_Plain_10);
oledOK = true;
display.clear();
display.drawString(0, 0, "System Reset...");
display.display();
}
// 🔥 initializeRTC ((translated))
checkRTCStatus();
// initialize(translated)sensor
if (lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE)) {
lightSensorOK = true;
delay(200);
}
// initializetemperaturesensor
tempSensors.begin();
delay(500);
tempSensors.requestTemperatures();
delay(200);
float tempC = tempSensors.getTempCByIndex(0);
if (tempC != -127.00 && tempC != 85.00) {
tempSensorOK = true;
}
// 🔥 (translated)sensor
readTurbidityAnalog();
turbidityOK = true; // (translated)sensor(translated)
// initializeSD(translated)
sdCardOK = initSDCard();
// connectWiFi
WiFi.begin(ssid, password);
int wifiRetries = 0;
while (WiFi.status() != WL_CONNECTED && wifiRetries < 20) {
delay(500);
wifiRetries++;
}
if (WiFi.status() == WL_CONNECTED) {
wifiOK = true;
Serial.println("WiFi連接成功");
// 🔥 (translated)NTPsync
syncTimeWithNTP();
lastNTPSyncTime = millis();
}
// display(translated)status
Serial.println("=== 系統狀態 ===");
Serial.println("OLED: " + String(oledOK ? "OK" : "ERR"));
Serial.println("溫度: " + String(tempSensorOK ? "OK" : "ERR"));
Serial.println("濁度(類比): " + String(turbidityOK ? "OK" : "ERR"));
Serial.println("光照: " + String(lightSensorOK ? "OK" : "ERR"));
Serial.println("RTC: " + String(rtcOK ? "OK" : "ERR"));
Serial.println("SD卡: " + String(sdCardOK ? "OK" : "ERR"));
Serial.println("WiFi: " + String(wifiOK ? "OK" : "ERR"));
Serial.println("NTP同步: " + String(ntpSynced ? "OK" : "ERR"));
if (oledOK) {
display.clear();
display.drawString(0, 0, "System Ready!");
display.drawString(0, 15, "SD:" + String(sdCardOK ? "OK" : "ERR") + " WiFi:" + String(wifiOK ? "OK" : "ERR"));
display.drawString(0, 25, "Temp:" + String(tempSensorOK ? "OK" : "ERR") + "Turbidity(A):" + String(turbidityOK ? "OK" : "ERR"));
display.drawString(0, 35, "Lux:" + String(lightSensorOK ? "OK" : "ERR"));
display.drawString(0, 45, "RTC:" + String(rtcOK ? "OK" : "ERR"));
display.display();
delay(3000);
}
Serial.println("系統初始化完成");
Serial.println("當前濁度: " + String(latestTurbidityNTU, 1) + " NTU, " + String(latestTurbidityVoltage, 3) + "V");
}
// 🔥 updatesensor(translated) ((translated))
void updateSensorData() {
// (translated) - (translated)
if (rtcOK) {
DateTime now = rtc.now();
latestDateString = getDateString(now);
latestTimeString = getTimeString(now);
latestDateTime = latestDateString + " " + latestTimeString;
} else if (hasValidBootTime) {
// (translated) + (translated)
unsigned long currentTime = bootTime + (millis() / 1000);
DateTime estimatedTime = DateTime(currentTime);
latestDateString = getDateString(estimatedTime);
latestTimeString = getTimeString(estimatedTime);
latestDateTime = latestDateString + " " + latestTimeString + " (估計)";
} else {
// (translated)
unsigned long seconds = millis() / 1000;
unsigned long minutes = seconds / 60;
unsigned long hours = minutes / 60;
unsigned long days = hours / 24;
char timeStr[30];
if (days > 0) {
sprintf(timeStr, "%lu天 %02lu:%02lu:%02lu", days, hours % 24, minutes % 60, seconds % 60);
} else {
sprintf(timeStr, "%02lu:%02lu:%02lu", hours % 24, minutes % 60, seconds % 60);
}
latestTimeString = String(timeStr);
latestDateTime = "運行時間 " + latestTimeString;
latestDateString = "----/--/--";
}
// readtemperature
if (tempSensorOK) {
tempSensors.requestTemperatures();
delay(200);
latestTemperature = tempSensors.getTempCByIndex(0);
if (latestTemperature == -127.00 || latestTemperature == 85.00) {
tempSensorOK = false;
latestTemperature = -999;
}
}
// 🔥 read(translated)
readTurbidityAnalog();
// read(translated)
if (lightSensorOK) {
latestLightLevel = lightMeter.readLightLevel();
if (latestLightLevel < 0) {
lightSensorOK = false;
latestLightLevel = 0;
}
}
}
// 🔥 SD(translated)record ((translated) - (translated)record(translated)NTU(translated)voltage)
void logDataToSD() {
if (!sdCardOK) return;
sdLogCounter++;
File dataFile = SD.open(logFileName, FILE_APPEND);
if (dataFile) {
String logEntry = latestDateTime + "," +
String(latestTurbidityNTU, 2) + "," +
String(latestTurbidityVoltage, 3);
dataFile.println(logEntry);
dataFile.close();
Serial.println("SD記錄 #" + String(sdLogCounter) + ": " + logEntry);
}
}
// upload(translated)IFTTT
void uploadDataToIFTTT() {
if (WiFi.status() != WL_CONNECTED) return;
uploadCounter++;
HTTPClient http;
String allData = "時間: " + latestDateTime +
" | 溫度: " + String(latestTemperature, 2) + "°C" +
" | 濁度: " + String(latestTurbidityNTU, 1) + "NTU" +
" | 電壓: " + String(latestTurbidityVoltage, 3) + "V" +
" | 光照: " + String(latestLightLevel, 1) + "lx";
String postData = "value1=" + urlEncode(allData) + "&value2=&value3=";
http.begin(iftttUrl);
http.addHeader("Content-Type", "application/x-www-form-urlencoded");
int httpResponseCode = http.POST(postData);
if (httpResponseCode == 200) {
Serial.println("IFTTT上傳成功 #" + String(uploadCounter));
} else {
Serial.println("IFTTT上傳失敗: " + String(httpResponseCode));
}
http.end();
}
void loop() {
unsigned long currentTime = millis();
// 🔥 (translated)NTPsync
if (currentTime - lastNTPSyncTime >= ntpSyncInterval && wifiOK) {
lastNTPSyncTime = currentTime;
syncTimeWithNTP();
}
// (translated)temperaturesensor
if (currentTime - lastTempCheckTime >= tempCheckInterval && !tempSensorOK) {
lastTempCheckTime = currentTime;
tempSensors.begin();
delay(500);
tempSensors.requestTemperatures();
delay(200);
float tempC = tempSensors.getTempCByIndex(0);
if (tempC != -127.00 && tempC != 85.00) {
tempSensorOK = true;
}
}
// updatesensor(translated)
if (currentTime - previousSensorTime >= sensorInterval) {
previousSensorTime = currentTime;
updateSensorData();
}
// updateOLEDdisplay
if (currentTime - previousDisplayTime >= displayInterval) {
previousDisplayTime = currentTime;
displayPage = (displayPage + 1) % 4;
updateOLEDDisplay();
}
// upload(translated)IFTTT
if (currentTime - previousUploadTime >= uploadInterval && wifiOK) {
previousUploadTime = currentTime;
uploadDataToIFTTT();
}
// record(translated)SD(translated)
if (currentTime - previousSDLogTime >= sdLogInterval && sdCardOK) {
previousSDLogTime = currentTime;
logDataToSD();
}
// (translated)
if (Serial.available()) {
String command = Serial.readString();
command.trim();
command.toLowerCase();
if (command == "turbidity" || command == "ntu") {
Serial.println("=== 濁度測試 (類比模式) ===");
readTurbidityAnalog();
Serial.println("ADC原始值: " + String(turbidityRawValue));
Serial.println("電壓值: " + String(latestTurbidityVoltage, 3) + "V");
Serial.println("NTU值: " + String(latestTurbidityNTU, 2) + " NTU");
}
else if (command == "sd") {
if (sdCardOK) {
logDataToSD();
} else {
sdCardOK = initSDCard();
}
}
else if (command == "sync" || command == "ntp") {
Serial.println("手動NTP同步...");
syncTimeWithNTP();
}
else if (command == "rtc") {
Serial.println("檢查RTC狀態...");
checkRTCStatus();
}
else if (command == "status") {
Serial.println("=== 系統狀態 ===");
Serial.println("OLED: " + String(oledOK ? "OK" : "ERR"));
Serial.println("溫度: " + String(tempSensorOK ? "OK" : "ERR"));
Serial.println("濁度(類比): " + String(turbidityOK ? "OK" : "ERR"));
Serial.println("光照: " + String(lightSensorOK ? "OK" : "ERR"));
Serial.println("RTC: " + String(rtcOK ? "OK" : "ERR"));
Serial.println("SD卡: " + String(sdCardOK ? "OK" : "ERR"));
Serial.println("WiFi: " + String(wifiOK ? "OK" : "ERR"));
Serial.println("NTP同步: " + String(ntpSynced ? "OK" : "ERR"));
Serial.println("當前時間: " + latestDateTime);
Serial.println("當前濁度: " + String(latestTurbidityNTU, 2) + " NTU");
Serial.println("當前電壓: " + String(latestTurbidityVoltage, 3) + "V");
}
else if (command == "help") {
Serial.println("=== 可用命令 ===");
Serial.println("turbidity/ntu - 測試濁度(類比)");
Serial.println("sd - 測試SD卡");
Serial.println("sync/ntp - 手動NTP同步");
Serial.println("rtc - 檢查RTC狀態");
Serial.println("status - 系統狀態");
Serial.println("help - 顯示幫助");
}
}
// (translated)WiFistatus
if (WiFi.status() != WL_CONNECTED && wifiOK) {
wifiOK = false;
WiFi.reconnect();
} else if (WiFi.status() == WL_CONNECTED && !wifiOK) {
wifiOK = true;
}
delay(100);
}
// 🔥 OLEDdisplayupdate ((translated)displayNTU(translated)voltage)
void updateOLEDDisplay() {
if (!oledOK) return;
display.clear();
switch (displayPage) {
case 0: // (translated) ((translated)displayNTU(translated)voltage)
display.setFont(ArialMT_Plain_10);
display.setTextAlignment(TEXT_ALIGN_CENTER);
display.drawString(64, 0, "=== TURBIDITY ===");
display.drawString(64, 12, latestTimeString);
if (turbidityOK) {
display.setFont(ArialMT_Plain_16);
display.drawString(64, 24, String(latestTurbidityNTU, 1) + " NTU"); // NTU(translated)
display.setFont(ArialMT_Plain_10);
display.drawString(64, 40, String(latestTurbidityVoltage, 3) + "V"); // voltage(translated)
display.drawString(64, 52, "ADC: " + String(turbidityRawValue)); // ADC(translated)
} else {
display.setFont(ArialMT_Plain_24);
display.drawString(64, 35, "ERROR");
}
break;
case 1: // (translated)
display.setFont(ArialMT_Plain_10);
display.setTextAlignment(TEXT_ALIGN_CENTER);
display.drawString(64, 0, "=== TIME ===");
if (rtcOK) {
DateTime now = rtc.now();
display.setFont(ArialMT_Plain_10);
display.drawString(64, 15, latestDateString);
display.setFont(ArialMT_Plain_16);
display.drawString(64, 32, latestTimeString);
display.setFont(ArialMT_Plain_10);
display.drawString(64, 50, daysOfTheWeek[now.dayOfTheWeek()]);
} else {
display.setFont(ArialMT_Plain_16);
display.drawString(64, 20, latestDateString);
display.setFont(ArialMT_Plain_16);
display.drawString(64, 40, latestTimeString);
}
break;
case 2: // (translated)
display.setFont(ArialMT_Plain_10);
display.setTextAlignment(TEXT_ALIGN_CENTER);
display.drawString(64, 0, "=== LIGHT ===");
display.drawString(64, 14, latestTimeString);
if (lightSensorOK) {
display.setFont(ArialMT_Plain_24);
display.drawString(64, 28, String(latestLightLevel, 0) + " lux");
} else {
display.setFont(ArialMT_Plain_24);
display.drawString(64, 35, "ERROR");
}
break;
case 3: // temperature(translated)
display.setFont(ArialMT_Plain_10);
display.setTextAlignment(TEXT_ALIGN_CENTER);
display.drawString(64, 0, "=== TEMP ===");
display.drawString(64, 14, latestTimeString);
if (latestTemperature == -999) {
display.setFont(ArialMT_Plain_24);
display.drawString(64, 35, "ERROR");
} else {
display.setFont(ArialMT_Plain_24);
display.drawString(64, 28, String(latestTemperature, 1));
display.setFont(ArialMT_Plain_16);
display.drawString(64, 50, "°C");
}
break;
}
display.display();
}
// (translated)
String getDateString(const DateTime& dt) {
char dateStr[12];
sprintf(dateStr, "%04u/%02u/%02u", dt.year(), dt.month(), dt.day());
return String(dateStr);
}
String getTimeString(const DateTime& dt) {
char timeStr[10];
sprintf(timeStr, "%02u:%02u:%02u", dt.hour(), dt.minute(), dt.second());
return String(timeStr);
}
String urlEncode(String str) {
String encodedString = "";
char c;
for (int i = 0; i < str.length(); i++) {
c = str.charAt(i);
if (c == ' ') {
encodedString += '+';
} else if (isalnum(c)) {
encodedString += c;
} else {
char code0, code1;
code1 = (c & 0xf) + '0';
if ((c & 0xf) > 9) code1 = (c & 0xf) - 10 + 'A';
c = (c >> 4) & 0xf;
code0 = c + '0';
if (c > 9) code0 = c - 10 + 'A';
encodedString += '%';
encodedString += code0;
encodedString += code1;
}
}
return encodedString;
}
/*
* === (translated) ((translated)) ===
*
* 🔥 (translated):
* 1. (translated)sensor(translated) (ADCread)
* 2. OLED(translated)display:
* - NTU(translated) ((translated)voltage(translated))
* - voltage(translated) (V)
* - ADC(translated)
*
* 🔥 (translated)sensor(translated):
* - (translated) analogRead() read 0-4095 ADC(translated)
* - convert(translated) 0-3.3V voltage(translated)
* - (translated)voltage(translated) NTU (translated)
* - (translated)readaverage, (translated)
*
* 🔥 (translated)SD(translated)record:
* - (translated)record(translated), NTU(translated), voltage(translated)
* - format:Date(Time),Turbidity(NTU),Turbidity(Voltage)
*
* 🔥 NTU(translated):
* - voltage > 2.5V: 0 NTU ((translated))
* - voltage 2.0-2.5V: 0-50 NTU
* - voltage 1.5-2.0V: 50-150 NTU
* - voltage 1.0-1.5V: 150-350 NTU
* - voltage < 1.0V: 350-1000 NTU
*
* ✅ (translated):
* - OLED(translated)display
* - IFTTT(translated)upload ((translated)NTU(translated)voltage(translated))
* - NTP(translated)sync
* - (translated)
* - (translated)sensor(translated)
*
* 📊 OLEDdisplay(translated):
* Page 0: (translated) - NTU(translated)/voltage(translated)/ADC(translated)
* Page 1: (translated) - (translated)/(translated)/(translated)
* Page 2: (translated) - brightness(translated)
* Page 3: temperature - temperature(translated)
*
* 💾 SD(translated)recordformat:
* Date(Time),Turbidity(NTU),Turbidity(Voltage)
*
* 📡 IFTTTupload(translated):
* (translated) | temperature | (translated)(NTU) | voltage | (translated)
*
* 🔧 (translated):
* - turbidity/ntu: test(translated)sensor (displayADC/voltage/NTU)
* - sd: testSD(translated)record
* - sync/ntp: (translated)NTPsync
* - rtc: (translated)RTCstatus
* - status: display(translated)status
* - help: display(translated)
*/
ESP32_INA3221_X6_F1.ino
/*
* ESP32 INA3221 currentvoltage(translated) ((translated)OLEDdisplay(translated)IFTTT(translated)upload)
* hardware:ESP32-WROOM-32, INA3221currentsensor, SSD1306 OLEDdisplay(translated)
* (translated):(translated)currentvoltage(translated), OLED(translated)display, IFTTT(translated)upload
* version:(translated) IFTTT format(translated)display
*/
#include "Adafruit_INA3221.h"
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <WiFi.h>
#include <HTTPClient.h>
// WiFi setup
const char* ssid = "TP-Link_6D5A";
const char* password = "********";
// IFTTT setup - (translated)HTTP(translated)HTTPS
const String iftttEvent = "water_quality_data"; // (translated)IFTTT(translated)
const String iftttKey = "**************"; // (translated)IFTTT Webhook(translated)
const String iftttUrl = "http://maker.ifttt.com/trigger/" + iftttEvent + "/with/key/" + iftttKey; // (translated)HTTP
// OLED settings
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C
// (translated)
Adafruit_INA3221 ina3221;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// display(translated)
int displayMode = 0;
const int maxDisplayModes = 4;
unsigned long lastModeChange = 0;
const unsigned long modeInterval = 2000; // 2 seconds(translated)
// (translated)variable
unsigned long previousSensorTime = 0;
const long sensorInterval = 1000; // sensorupdate(translated)1 seconds
unsigned long previousUploadTime = 0;
const long uploadInterval = 180000; // upload(translated)IFTTT(translated)30 seconds
// (translated)status(translated)
bool ina3221OK = false;
bool oledOK = false;
bool wifiOK = false;
// (translated)sensor(translated)
float latestVoltage[3] = {0, 0, 0};
float latestCurrent[3] = {0, 0, 0};
float latestPower[3] = {0, 0, 0};
float latestTotalPower = 0;
// upload(translated)status
int uploadCounter = 0;
bool uploadSuccess = false;
unsigned long uploadStatusTime = 0;
void setup() {
Serial.begin(115200);
delay(1000);
Serial.println("\n\n=== ESP32 INA3221 電流電壓監控程式 (IFTTT雲端版) ===");
Serial.println("-----------------------------------");
// initializeOLEDdisplay(translated)
Serial.print("初始化OLED顯示器...");
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println("失敗!");
} else {
Serial.println("成功!");
oledOK = true;
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(1); // (translated)1(translated)WHITE
display.setCursor(0,0);
display.println("系統初始化中...");
display.display();
}
// initialize INA3221
Serial.print("初始化INA3221電流感測器...");
if (!ina3221.begin(0x40, &Wire)) {
Serial.println("失敗! 無法找到 INA3221!");
if (oledOK) {
display.setCursor(0, 20);
display.println("INA3221: 失敗");
display.display();
}
} else {
Serial.println("成功!");
ina3221OK = true;
if (oledOK) {
display.setCursor(0, 30);
display.println("INA3221: OK");
display.display();
}
// settings INA3221 ((translated) 0, 1, 2)
ina3221.setAveragingMode(INA3221_AVG_16_SAMPLES);
ina3221.setShuntResistance(0, 0.1); // CH0 - 0.1Ω (translated)
ina3221.setShuntResistance(1, 0.1); // CH1 - 0.1Ω (translated)
ina3221.setShuntResistance(2, 0.1); // CH2 - 0.1Ω (translated) ((translated))
Serial.println("INA3221 設定完成:");
Serial.println("• CH1: 0.1Ω 分流電阻");
Serial.println("• CH2: 0.1Ω 分流電阻");
Serial.println("• CH3: 0.1Ω 分流電阻");
}
// connectWiFi
Serial.print("連接WiFi...");
if (oledOK) {
display.setCursor(0, 20);
display.println("WiFi: Connect...?");
display.display();
}
WiFi.begin(ssid, password);
int wifiRetries = 0;
while (WiFi.status() != WL_CONNECTED && wifiRetries < 20) {
delay(500);
Serial.print(".");
wifiRetries++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nWiFi連接成功!");
Serial.print("IP地址: ");
Serial.println(WiFi.localIP());
Serial.print("信號強度: ");
Serial.print(WiFi.RSSI());
Serial.println(" dBm");
wifiOK = true;
if (oledOK) {
display.setCursor(0, 20);
display.println("WiFi: Connect OK");
display.setCursor(0, 30);
display.print("IP: ");
display.println(WiFi.localIP());
display.display();
}
} else {
Serial.println("\nWiFi連接失敗!");
if (oledOK) {
display.setCursor(0, 20);
display.println("WiFi: 失敗");
display.display();
}
}
// display(translated)status(translated)
Serial.println("\n=== 系統狀態摘要 ===");
Serial.println("OLED顯示器: " + String(oledOK ? "正常" : "失敗"));
Serial.println("INA3221感測器: " + String(ina3221OK ? "正常" : "失敗"));
Serial.println("WiFi連接: " + String(wifiOK ? "正常" : "失敗"));
Serial.println("-----------------------------------");
// (translated)OLED(translated)display(translated)status(translated)
if (oledOK) {
display.clearDisplay();
display.setCursor(0, 0);
display.println("System Status:");
display.print("INA3221: ");
display.println(ina3221OK ? "OK" : "ERR");
display.print("WiFi: ");
display.println(wifiOK ? "OK" : "ERR");
display.println("");
display.println("CH1: Clear");
display.println("CH2: Clear");
display.println("CH3: Clear");
display.display();
delay(3000);
}
// displayIFTTT setup(translated)
Serial.println("\n=== IFTTT雲端設置 ===");
Serial.println("事件名稱: " + iftttEvent);
Serial.println("Webhook密鑰: " + iftttKey);
Serial.println("上傳間隔: 3分鐘");
Serial.println("-----------------------------------");
Serial.println("\n系統初始化完成!開始監控...");
Serial.println("可用指令:");
Serial.println("• 輸入 'upload' 手動觸發上傳");
Serial.println("• 輸入 'status' 查看系統狀態");
}
void loop() {
unsigned long currentTime = millis();
// (translated)input
if (Serial.available() > 0) {
String command = Serial.readStringUntil('\n');
command.trim();
if (command == "upload") {
Serial.println("手動觸發上傳...");
if (wifiOK && ina3221OK) {
uploadDataToIFTTT();
} else {
Serial.println("系統未就緒,無法上傳 (WiFi: " + String(wifiOK) + ", INA3221: " + String(ina3221OK) + ")");
}
}
else if (command == "status") {
printSystemStatus();
}
}
// (translated)updatesensor(translated)
if (currentTime - previousSensorTime >= sensorInterval) {
previousSensorTime = currentTime;
if (ina3221OK) {
updateSensorData();
}
}
// (translated)display(translated)
if (currentTime - lastModeChange > modeInterval) {
displayMode = (displayMode + 1) % maxDisplayModes;
lastModeChange = currentTime;
}
// updateOLEDdisplay
if (oledOK) {
updateOLEDDisplay();
}
// (translated)upload(translated)IFTTT
if (currentTime - previousUploadTime >= uploadInterval) {
previousUploadTime = currentTime;
if (wifiOK && ina3221OK) {
uploadDataToIFTTT();
}
}
// (translated)WiFiconnectstatus
if (WiFi.status() != WL_CONNECTED && wifiOK) {
Serial.println("WiFi連接已斷開,嘗試重新連接...");
wifiOK = false;
WiFi.reconnect();
} else if (WiFi.status() == WL_CONNECTED && !wifiOK) {
Serial.println("WiFi已重新連接");
wifiOK = true;
}
delay(100); // (translated)delayavoid(translated)CPU
}
// update(translated)sensor(translated)
void updateSensorData() {
// read(translated)
latestTotalPower = 0;
for (int ch = 0; ch < 3; ch++) {
latestVoltage[ch] = ina3221.getBusVoltage(ch);
latestCurrent[ch] = ina3221.getCurrentAmps(ch) * 1000; // convert(translated) mA
latestPower[ch] = latestVoltage[ch] * latestCurrent[ch]; // mW
latestTotalPower += latestPower[ch];
}
// (translated)5 secondsoutput(translated)
static unsigned long lastDetailedOutput = 0;
if (millis() - lastDetailedOutput > 5000) {
lastDetailedOutput = millis();
printDetailedData();
}
}
// output(translated) - (translated)display CH1, CH2, CH3
void printDetailedData() {
Serial.println("\n=== INA3221 三通道監控數據 ===");
Serial.println("CH | 電壓(V) | 電流(mA) | 功率(mW) | 狀態");
Serial.println("---|---------|----------|----------|--------");
for (int ch = 0; ch < 3; ch++) {
Serial.print(ch + 1); // display(translated) CH1, CH2, CH3
Serial.print(" | ");
Serial.print(latestVoltage[ch], 3);
Serial.print(" | ");
Serial.print(latestCurrent[ch], 1);
Serial.print(" | ");
Serial.print(latestPower[ch], 1);
Serial.print(" | ");
if (abs(latestCurrent[ch]) < 0.1) {
Serial.println("關閉");
} else if (latestCurrent[ch] < 10) {
Serial.println("睡眠");
} else if (latestCurrent[ch] < 50) {
Serial.println("低功耗");
} else if (latestCurrent[ch] < 100) {
Serial.println("正常");
} else {
Serial.println("高負載");
}
}
Serial.print("總功耗: ");
Serial.print(latestTotalPower, 1);
Serial.println(" mW");
Serial.print("運行時間: ");
Serial.print(millis() / 1000);
Serial.println(" 秒");
Serial.println("================================");
}
// updateOLEDdisplay
void updateOLEDDisplay() {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(1); // (translated)1(translated)WHITE
switch (displayMode) {
case 0:
displayOverview();
break;
case 1:
displayChannel(0);
break;
case 2:
displayChannel(1);
break;
case 3:
displayChannel(2);
break;
}
display.display();
}
// display(translated) - (translated)display CH1, CH2, CH3
void displayOverview() {
display.setCursor(0, 0);
display.println("=== ALL CHANNELS ===");
for (int ch = 0; ch < 3; ch++) {
display.setCursor(0, 16 + ch * 10);
display.print("CH");
display.print(ch + 1); // display(translated) CH1, CH2, CH3
display.print(": ");
if (abs(latestCurrent[ch]) < 0.1) {
display.println("OFF");
} else {
display.print((int)latestCurrent[ch]);
display.print("mA ");
display.print(latestVoltage[ch], 1);
display.println("V");
}
}
// display(translated)
display.setCursor(0, 46);
display.print("Total: ");
display.print((int)latestTotalPower);
display.println("mW");
// display(translated)status
display.setCursor(0, 57);
display.print("Time:");
display.print(millis() / 1000);
display.print("s U:");
display.print(uploadCounter);
// display(translated)status(translated)uploadstatus
display.setCursor(100, 0);
display.print(ina3221OK ? "I" : "-");
display.print(wifiOK ? "W" : "-");
// displayuploadstatus (successdisplay Update OK, (translated)display Update ER, 5 seconds(translated))
if (millis() - uploadStatusTime < 5000) {
display.setCursor(65, 64);
if (uploadSuccess) {
display.print("UD OK");
} else {
display.print("UD ER");
}
}
}
// display(translated) - (translated)display CH1, CH2, CH3
void displayChannel(int channelNum) {
display.setCursor(0, 0);
display.setTextSize(2);
display.print("CH");
display.print(channelNum + 1); // display(translated) CH1, CH2, CH3
display.setTextSize(1);
// voltagedisplay
display.setCursor(0, 20);
display.print("V: ");
display.print(latestVoltage[channelNum], 3);
display.println(" V");
// currentdisplay
display.setCursor(0, 30);
display.print("I: ");
display.print(latestCurrent[channelNum], 1);
display.println(" mA");
// powerdisplay
display.setCursor(0, 40);
display.print("P: ");
display.print(latestPower[channelNum], 1);
display.println(" mW");
// status(translated)
display.setCursor(0, 50);
if (abs(latestCurrent[channelNum]) < 0.1) {
display.println("Status: OFF");
} else if (latestCurrent[channelNum] < 10) {
display.println("Status: NORMAL");
} else if (latestCurrent[channelNum] < 50) {
display.println("Status: LOW");
} else if (latestCurrent[channelNum] < 100) {
display.println("Status: NORMAL");
} else if (latestCurrent[channelNum] < 200) {
display.println("Status: HIGH");
} else {
display.println("Status: PEAK");
}
// display(translated)status
display.setCursor(100, 0);
display.print(ina3221OK ? "I" : "-");
display.print(wifiOK ? "W" : "-");
// displayuploadstatus (Update OK/ER)
if (millis() - uploadStatusTime < 5000) {
display.setCursor(60, 50);
if (uploadSuccess) {
display.print("Update OK");
} else {
display.print("Update ER");
}
}
}
// upload(translated)IFTTT - (translated) value1 (translated) value2 (translated), (translated) CH1, CH2, CH3
void uploadDataToIFTTT() {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("無法上傳數據:WiFi未連接");
uploadSuccess = false;
uploadStatusTime = millis();
return;
}
uploadCounter++; // (translated)upload(translated)
Serial.println("\n=== 開始IFTTT上傳 #" + String(uploadCounter) + " ===");
Serial.println("WiFi狀態: " + String(WiFi.status()));
Serial.println("IP地址: " + WiFi.localIP().toString());
Serial.println("信號強度: " + String(WiFi.RSSI()) + " dBm");
HTTPClient http;
// (translated)data(translated) JSON string, (translated) value3, (translated) CH1, CH2, CH3
String jsonData = "{";
jsonData += "\"CH1_V\":" + String(latestVoltage[0], 2) + ",";
jsonData += "\"CH1_mA\":" + String(latestCurrent[0], 1) + ",";
jsonData += "\"CH1_mW\":" + String(latestPower[0], 1) + ",";
jsonData += "\"CH2_V\":" + String(latestVoltage[1], 2) + ",";
jsonData += "\"CH2_mA\":" + String(latestCurrent[1], 1) + ",";
jsonData += "\"CH2_mW\":" + String(latestPower[1], 1) + ",";
jsonData += "\"CH3_V\":" + String(latestVoltage[2], 2) + ",";
jsonData += "\"CH3_mA\":" + String(latestCurrent[2], 1) + ",";
jsonData += "\"CH3_mW\":" + String(latestPower[2], 1) + ",";
jsonData += "\"Total_mW\":" + String(latestTotalPower, 1) + ",";
jsonData += "\"Uptime\":" + String(millis() / 1000) + ",";
jsonData += "\"WiFi_RSSI\":" + String(WiFi.RSSI());
jsonData += "}";
// (translated)GET(translated)URL - value2 (translated) value3 (translated)string
String url = iftttUrl;
url += "?value1=" + urlEncode(jsonData);
url += "&value2="; // (translated)
url += "&value3="; // (translated)
Serial.println("上傳URL: " + url);
// (translated)HTTPconnect
http.begin(url);
http.setTimeout(5000); // setup5 seconds(translated)
// (translated)GET(translated)
unsigned long startTime = millis();
int httpResponseCode = http.GET();
unsigned long endTime = millis();
Serial.println("請求耗時: " + String(endTime - startTime) + " ms");
if (httpResponseCode > 0) {
String response = http.getString();
Serial.println("IFTTT上傳成功,響應碼: " + String(httpResponseCode));
Serial.println("響應內容: " + response);
uploadSuccess = true;
} else {
Serial.println("IFTTT上傳失敗,錯誤碼: " + String(httpResponseCode));
uploadSuccess = false;
}
// recorduploadstatus(translated), (translated)OLEDdisplay
uploadStatusTime = millis();
http.end();
Serial.println("=== IFTTT上傳結束 ===\n");
}
// output(translated)status
void printSystemStatus() {
Serial.println("\n=== 系統狀態報告 ===");
Serial.println("INA3221感測器: " + String(ina3221OK ? "正常" : "失敗"));
Serial.println("OLED顯示器: " + String(oledOK ? "正常" : "失敗"));
Serial.println("WiFi連接: " + String(wifiOK ? "正常" : "失敗"));
if (wifiOK) {
Serial.println("IP地址: " + WiFi.localIP().toString());
Serial.println("信號強度: " + String(WiFi.RSSI()) + " dBm");
}
Serial.println("運行時間: " + String(millis() / 1000) + " 秒");
Serial.println("上傳次數: " + String(uploadCounter));
Serial.println("最後上傳: " + String(uploadSuccess ? "成功" : "失敗"));
Serial.println("顯示模式: " + String(displayMode));
Serial.println("===================\n");
}
// URL(translated) - (translated)
String urlEncode(String str) {
String encodedString = "";
char c;
char code0;
char code1;
for (int i = 0; i < str.length(); i++) {
c = str.charAt(i);
if (c == ' ') {
encodedString += '+';
} else if (isAlphaNumeric(c)) {
encodedString += c;
} else {
code1 = (c & 0xf) + '0';
if ((c & 0xf) > 9) {
code1 = (c & 0xf) - 10 + 'A';
}
c = (c >> 4) & 0xf;
code0 = c + '0';
if (c > 9) {
code0 = c - 10 + 'A';
}
encodedString += '%';
encodedString += code0;
encodedString += code1;
}
}
return encodedString;
}
ESP32_MP3_PAM8403_WIFI_OLED_MIC_T1.ino
/*
* ESP32 (translated) + IFTTT(translated) + MAX9814volume(translated) + OLED(translated)display
* (translated): OLED(translated)display + DFPlayer(translated) + MAX9814volume(translated) + IFTTT(translated)
*
* hardware(translated):
* ESP32 → DFPlayer Mini ((translated)5V(translated)):
* - GPIO16 → DFPlayer RX
* - GPIO17 → DFPlayer TX
* - GND → DFPlayer GND ((translated))
* - (translated)5V → DFPlayer VCC
*
* ESP32 → OLED (I2C):
* - GPIO21 (SDA) → OLED SDA
* - GPIO22 (SCL) → OLED SCL
* - 3.3V → OLED VCC, GND → OLED GND
*
* ESP32 → MAX9814microphone:
* - GPIO34 (ADC) → MAX9814 OUT
* - 3.3V → MAX9814 VCC, GND → MAX9814 GND
* - ARpin(translated)GPIO32(translated)((translated))
*/
// ==================== (translated) ====================
#include <WiFi.h>
#include <HTTPClient.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "DFRobotDFPlayerMini.h"
// ==================== (translated) ====================
// OLEDdisplay(translated)settings
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C
// (translated)SSD1306(translated), (translated)
#ifndef SSD1306_WHITE
#define SSD1306_WHITE 1
#endif
#ifndef SSD1306_BLACK
#define SSD1306_BLACK 0
#endif
#ifndef SSD1306_SWITCHCAPVCC
#define SSD1306_SWITCHCAPVCC 0x02
#endif
// Pin definitions
#define MIC_PIN 34 // MAX9814 OUT
#define MIC_GAIN_PIN 32 // MAX9814 AR ((translated), (translated))
#define LED_PIN 2
// MAX9814 settings
#define SAMPLE_WINDOW 50 // (translated) ((translated)seconds)
#define SAMPLE_RATE 1000 // (translated) (Hz)
#define VOLUME_SAMPLES 20 // volume(translated)
// OLED (translated)settings
#define TOTAL_PAGES 4 // (translated) ((translated))
#define PAGE_DURATION 5000 // (translated)display(translated) ((translated)seconds) - (translated)5 seconds
#define IFTTT_PAGE_DURATION 2000 // IFTTT(translated)display(translated) ((translated)seconds) - 2 seconds
// ==================== networksettings ====================
// WiFi setup
const char* ssid = "TP-Link_6D5A";
const char* password = "********";
const String iftttEvent = "water_quality_data";
const String iftttKey = "****************";
const String iftttUrl = "http://maker.ifttt.com/trigger/" + iftttEvent + "/with/key/" + iftttKey; // (translated)HTTP
// ==================== (translated)initialize ====================
HardwareSerial myHardwareSerial(1);
DFRobotDFPlayerMini myDFPlayer;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// ==================== (translated)variable ====================
// (translated)settings
int currentTrack = 1;
const int totalTracks = 3;
unsigned long lastPlayTime = 0;
const unsigned long trackDuration = 30000; // 30 seconds(translated)
bool isPlaying = false;
bool systemReady = false;
// volume(translated)
int currentVolume = 20;
// MAX9814 volume(translated)
float currentSoundLevel = 0.0; // (translated)volume (dB)
float peakSoundLevel = 0.0; // (translated)volume
float avgSoundLevel = 0.0; // averagevolume
int rawADCValue = 0; // (translated)ADC(translated)
float volumeBuffer[VOLUME_SAMPLES]; // volume(translated)
int bufferIndex = 0; // (translated)
bool volumeBufferFull = false; // (translated)
unsigned long lastVolumeUpdate = 0;
unsigned long lastVolumeSerial = 0; // (translated):serial portvolumedisplay(translated)
// (translated) ((translated)MAX9814)
int noiseThreshold = 45; // dBthreshold
bool autoMode = false;
unsigned long lastNoiseCheck = 0;
// WiFi(translated)IFTTT
bool wifiConnected = false;
unsigned long lastIFTTT = 0;
bool lastIFTTTSuccess = false;
String lastIFTTTEvent = "";
String lastIFTTTTime = ""; // (translated):(translated)IFTTT(translated)
int iftttSuccessCount = 0; // (translated):success(translated)
int iftttFailCount = 0; // (translated):(translated)
// OLED (translated)display
int currentPage = 0;
unsigned long lastPageChange = 0;
bool manualPageMode = false; // (translated)
// display(translated)
unsigned long lastDisplay = 0;
String systemStatus = "初始化...";
// error(translated)
int sdErrorCount = 0;
const int maxSDErrors = 3;
bool dfPlayerOnline = false;
bool oledOnline = false;
bool max9814Online = false;
void setup() {
Serial.begin(115200);
Serial.println("🎵 ESP32 完整音響+MAX9814+IFTTT系統");
Serial.println("=====================================");
pinMode(LED_PIN, OUTPUT);
pinMode(MIC_PIN, INPUT);
pinMode(MIC_GAIN_PIN, OUTPUT); // MAX9814(translated)
// settingsMAX9814(translated) (HIGH=60dB, LOW=50dB, (translated)=40dB)
digitalWrite(MIC_GAIN_PIN, LOW); // 50dB(translated)
// initializevolume(translated)
initVolumeBuffer();
// initializeOLED
initOLED();
// connectWiFi ((translated))
startWiFiConnection();
// initializeDFPlayer
initDFPlayerStable();
// testMAX9814
testMAX9814();
Serial.println("✅ 系統初始化完成!");
printHelp();
printSystemStatus();
}
void loop() {
// (translated)
if (isPlaying && systemReady) {
if (millis() - lastPlayTime > trackDuration) {
nextTrack();
}
}
// updateMAX9814volume(translated)
if (millis() - lastVolumeUpdate > 50) { // 20Hzupdate(translated)
updateSoundLevel();
lastVolumeUpdate = millis();
}
// MAX9814valueserial port(translated)display ((translated)2 secondsdisplay(translated))
if (millis() - lastVolumeSerial > 2000) {
printVolumeMonitor();
lastVolumeSerial = millis();
}
// (translated)DFPlayerstatus
handleDFPlayerStatus();
// (translated)
handleCommands();
// (translated)WiFi ((translated))
checkWiFi();
// (translated) ((translated)MAX9814)
if (millis() - lastNoiseCheck > 1000) { // (translated)seconds(translated)
handleAutoPlayWithMAX9814();
lastNoiseCheck = millis();
}
// OLED(translated) ((translated))
if (!manualPageMode) {
unsigned long pageDuration = (currentPage == 3) ? IFTTT_PAGE_DURATION : PAGE_DURATION;
if (millis() - lastPageChange > pageDuration) {
currentPage = (currentPage + 1) % TOTAL_PAGES;
lastPageChange = millis();
}
}
// updatedisplay
if (millis() - lastDisplay > 200) { // 5Hzupdate(translated)
updateDisplayPages();
lastDisplay = millis();
}
// statusLED
digitalWrite(LED_PIN, (millis() / (isPlaying ? 250 : 1000)) % 2);
delay(50);
}
// ==================== MAX9814 volume(translated) ====================
void initVolumeBuffer() {
for (int i = 0; i < VOLUME_SAMPLES; i++) {
volumeBuffer[i] = 0.0;
}
Serial.println("📊 MAX9814音量緩衝區初始化完成");
}
void testMAX9814() {
Serial.println("🎤 測試MAX9814麥克風...");
systemStatus = "測試麥克風";
// testread
int testReadings = 0;
for (int i = 0; i < 10; i++) {
int reading = analogRead(MIC_PIN);
if (reading > 0 && reading < 4095) {
testReadings++;
}
delay(100);
}
if (testReadings >= 5) {
max9814Online = true;
Serial.println("✅ MAX9814 檢測正常");
} else {
max9814Online = false;
Serial.println("⚠️ MAX9814 可能未正確連接");
}
}
void updateSoundLevel() {
long sum = 0;
int maxVal = 0;
int minVal = 4095;
// (translated)100(translated) ((translated))
for(int i = 0; i < 100; i++) {
int reading = analogRead(MIC_PIN);
sum += reading;
if(reading > maxVal) maxVal = reading;
if(reading < minVal) minVal = reading;
delayMicroseconds(100);
}
int average = sum / 100;
int amplitude = maxVal - minVal; // (translated)
rawADCValue = average; // saveaverage(translated)
// convert(translated)volume(translated) (0-80 (translated) 0-1000 (translated)range)
float soundLevel = map(amplitude, 0, 1000, 0, 80);
if (soundLevel < 0) soundLevel = 0;
if (soundLevel > 80) soundLevel = 80;
// (translated)volume(translated)
volumeBuffer[bufferIndex] = soundLevel;
bufferIndex = (bufferIndex + 1) % VOLUME_SAMPLES;
if (bufferIndex == 0) volumeBufferFull = true;
// (translated)average(translated)
calculateVolumeStats();
currentSoundLevel = soundLevel;
}
void calculateVolumeStats() {
float sum = 0.0;
float maxVal = 0.0;
int samples = volumeBufferFull ? VOLUME_SAMPLES : bufferIndex;
for (int i = 0; i < samples; i++) {
sum += volumeBuffer[i];
if (volumeBuffer[i] > maxVal) {
maxVal = volumeBuffer[i];
}
}
if (samples > 0) {
avgSoundLevel = sum / samples;
peakSoundLevel = maxVal;
}
}
// (translated):MAX9814valueserial port(translated)display
void printVolumeMonitor() {
if (max9814Online) {
Serial.println("🎤 ===== MAX9814 即時監控 =====");
Serial.printf(" 平均ADC: %4d/4095 (%.1f%%)\n", rawADCValue, (rawADCValue/4095.0)*100);
Serial.printf(" 振幅值: %4d (動態範圍)\n", (int)currentSoundLevel * 12.5); // (translated)
Serial.printf(" 當前音量: %5.1f/80\n", currentSoundLevel);
Serial.printf(" 平均音量: %5.1f/80\n", avgSoundLevel);
Serial.printf(" 峰值音量: %5.1f/80\n", peakSoundLevel);
Serial.printf(" 閾值設定: %5d %s\n", noiseThreshold, (avgSoundLevel > noiseThreshold) ? "🔊 超過" : "🔇 未達");
Serial.printf(" 自動模式: %s\n", autoMode ? "✅ 開啟" : "❌ 關閉");
// volume(translated)display ((translated))
int volume = map(currentSoundLevel, 0, 80, 0, 15);
Serial.print(" 音量條: ");
for(int i = 0; i < volume; i++) {
Serial.print("█");
}
Serial.println();
Serial.println("=============================");
} else {
Serial.println("🎤 MAX9814 離線中...");
}
}
void handleAutoPlayWithMAX9814() {
static int quietCount = 0;
static int loudCount = 0;
if (autoMode && max9814Online) {
if (avgSoundLevel > noiseThreshold) {
loudCount++;
quietCount = 0;
if (loudCount >= 2 && !isPlaying && systemReady) { // (translated)2 seconds(translated)
Serial.printf("🎵 MAX9814檢測到聲音(%.1fdB),啟動播放\n", avgSoundLevel);
startAutoRotation();
sendIFTTT("auto_play", "聲音檢測啟動播放", "音量: " + String(avgSoundLevel, 1) + "dB");
loudCount = 0;
}
} else {
quietCount++;
loudCount = 0;
if (quietCount >= 10 && isPlaying) { // (translated)10 seconds(translated)
Serial.printf("🤫 環境安靜(%.1fdB),暫停播放\n", avgSoundLevel);
myDFPlayer.pause();
isPlaying = false;
systemStatus = "自動暫停";
sendIFTTT("auto_pause", "環境安靜自動暫停", "音量: " + String(avgSoundLevel, 1) + "dB");
quietCount = 0;
}
}
}
}
// ==================== OLED (translated)display ====================
void updateDisplayPages() {
display.clearDisplay();
switch (currentPage) {
case 0:
drawPage1_MusicInfo();
break;
case 1:
drawPage2_VolumeInfo();
break;
case 2:
drawPage3_SystemStatus();
break;
case 3:
drawPage4_IFTTTStatus(); // (translated)
break;
}
// (translated)
drawPageIndicator();
display.display();
}
void drawPage1_MusicInfo() {
// (translated)
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println("♪ MUSIC PLAYER");
// WiFistatus
display.setCursor(100, 0);
display.println(wifiConnected ? "WiFi" : "----");
// (translated)
display.drawLine(0, 10, 128, 10, SSD1306_WHITE);
// (translated) ((translated))
display.setTextSize(3);
display.setCursor(35, 18);
display.printf("%02d", currentTrack);
// playstatus(translated)
display.setTextSize(2);
display.setCursor(5, 22);
if (isPlaying) {
display.println(">");
} else {
display.println("||");
}
// (translated) ((translated))
int progress = ((millis() - lastPlayTime) * 100) / trackDuration;
if (progress > 100) progress = 100;
display.drawRect(5, 45, 118, 8, SSD1306_WHITE);
display.fillRect(6, 46, (progress * 116) / 100, 6, SSD1306_WHITE);
// status(translated)
display.setTextSize(1);
display.setCursor(0, 55);
display.println(systemStatus);
}
void drawPage2_VolumeInfo() {
// (translated)
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println("🎤 VOLUME METER");
// MAX9814status
display.setCursor(100, 0);
display.println(max9814Online ? "MIC" : "---");
// (translated)
display.drawLine(0, 10, 128, 10, SSD1306_WHITE);
// (translated)volumevalue
display.setTextSize(1);
display.setCursor(0, 15);
display.printf("Live: %.1f dB", currentSoundLevel);
display.setCursor(0, 25);
display.printf("Avg: %.1f dB", avgSoundLevel);
display.setCursor(0, 35);
display.printf("Peak: %.1f dB", peakSoundLevel);
// (translated)volume(translated)
// (translated)volume(translated)
int currentBar = (currentSoundLevel * 100) / 80; // 0-80dB (translated) 0-100
if (currentBar > 100) currentBar = 100;
display.drawRect(0, 47, 128, 6, SSD1306_WHITE);
display.fillRect(1, 48, (currentBar * 126) / 100, 4, SSD1306_WHITE);
// averagevolume(translated)
int avgBar = (avgSoundLevel * 100) / 80;
if (avgBar > 100) avgBar = 100;
display.drawRect(0, 55, 128, 6, SSD1306_WHITE);
for (int i = 0; i < (avgBar * 126) / 100; i += 2) {
display.drawPixel(1 + i, 56, SSD1306_WHITE);
display.drawPixel(1 + i, 58, SSD1306_WHITE);
}
// threshold(translated)
int thresholdPos = (noiseThreshold * 126) / 80;
display.drawLine(thresholdPos, 47, thresholdPos, 61, SSD1306_WHITE);
}
void drawPage3_SystemStatus() {
// (translated)
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println("⚙ SYSTEM STATUS");
// IFTTTstatus
display.setCursor(100, 0);
display.println(lastIFTTTSuccess ? "IFTTT" : "----");
// (translated)
display.drawLine(0, 10, 128, 10, SSD1306_WHITE);
// modulestatus
display.setCursor(0, 15);
display.printf("DFPlayer: %s", dfPlayerOnline ? "OK" : "ERR");
display.setCursor(0, 25);
display.printf("OLED: %s", oledOnline ? "OK" : "ERR");
display.setCursor(0, 35);
display.printf("MAX9814: %s", max9814Online ? "OK" : "ERR");
display.setCursor(0, 45);
display.printf("WiFi: %s", wifiConnected ? "OK" : "ERR");
// (translated)IFTTT(translated)
display.setCursor(0, 55);
if (lastIFTTTEvent.length() > 0) {
String shortEvent = lastIFTTTEvent;
if (shortEvent.length() > 16) {
shortEvent = shortEvent.substring(0, 13) + "...";
}
display.printf("Last: %s", shortEvent.c_str());
} else {
display.println("Last: None");
}
}
// (translated):(translated) IFTTTstatus(translated)display
void drawPage4_IFTTTStatus() {
// (translated)
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println("📤 IFTTT STATUS");
// connectionstatus(translated)
display.setCursor(100, 0);
display.println(wifiConnected ? (lastIFTTTSuccess ? "OK" : "ERR") : "----");
// (translated)
display.drawLine(0, 10, 128, 10, SSD1306_WHITE);
// IFTTT(translated)
display.setTextSize(1);
display.setCursor(0, 15);
display.printf("Success: %d", iftttSuccessCount);
display.setCursor(0, 25);
display.printf("Failed: %d", iftttFailCount);
display.setCursor(0, 35);
display.printf("Total: %d", iftttSuccessCount + iftttFailCount);
// (translated)
display.setCursor(0, 45);
if (lastIFTTTEvent.length() > 0) {
String shortEvent = lastIFTTTEvent;
if (shortEvent.length() > 10) {
shortEvent = shortEvent.substring(0, 10);
}
display.printf("Event: %s", shortEvent.c_str());
} else {
display.println("Event: None");
}
// (translated)
display.setCursor(0, 55);
if (lastIFTTTTime.length() > 0) {
display.printf("Time: %s", lastIFTTTTime.c_str());
} else {
display.println("Time: --:--");
}
}
void drawPageIndicator() {
// (translated)
int dotY = 62;
int startX = 48; // (translated)4(translated)
for (int i = 0; i < TOTAL_PAGES; i++) {
int x = startX + i * 8;
if (i == currentPage) {
display.fillCircle(x, dotY, 2, SSD1306_WHITE);
} else {
display.drawCircle(x, dotY, 2, SSD1306_WHITE);
}
}
}
// ==================== DFPlayer(translated) ====================
void initDFPlayerStable() {
Serial.println("🎵 初始化DFPlayer...");
systemStatus = "初始化音樂";
myHardwareSerial.begin(9600, SERIAL_8N1, 16, 17);
delay(3000);
bool initSuccess = false;
for (int attempt = 0; attempt < 3; attempt++) {
if (myDFPlayer.begin(myHardwareSerial)) {
Serial.println("✅ DFPlayer 初始化成功!");
dfPlayerOnline = true;
initSuccess = true;
break;
} else {
Serial.printf("⚠️ DFPlayer初始化嘗試 %d/3 失敗\n", attempt + 1);
delay(2000);
}
}
if (!initSuccess) {
Serial.println("❌ DFPlayer 初始化失敗");
dfPlayerOnline = false;
return;
}
delay(1000);
myDFPlayer.volume(currentVolume);
delay(500);
myDFPlayer.outputDevice(DFPLAYER_DEVICE_SD);
delay(1500);
startAutoRotation();
sendIFTTT("system_start", "ESP32音響系統啟動", "DFPlayer已就緒");
}
void startAutoRotation() {
if (!dfPlayerOnline) return;
Serial.println("🚀 啟動自動輪播...");
currentTrack = 1;
playCurrentTrack();
isPlaying = true;
systemReady = true;
lastPlayTime = millis();
systemStatus = "輪播中";
}
void playCurrentTrack() {
if (!dfPlayerOnline) return;
Serial.printf("🎵 播放: Track %d\n", currentTrack);
myDFPlayer.play(currentTrack);
lastPlayTime = millis();
if (millis() - lastIFTTT > 15000) { // (translated)IFTTTfrequency
sendIFTTT("music_play", "播放音樂", "Track " + String(currentTrack));
}
}
void nextTrack() {
currentTrack++;
if (currentTrack > totalTracks) {
currentTrack = 1;
Serial.println("🔄 輪播循環");
}
Serial.printf("⏭️ 切換到: Track %d\n", currentTrack);
playCurrentTrack();
}
void handleDFPlayerStatus() {
if (!dfPlayerOnline || !myDFPlayer.available()) return;
uint8_t type = myDFPlayer.readType();
int value = myDFPlayer.read();
switch (type) {
case DFPlayerPlayFinished:
Serial.println("✅ 歌曲完成,自動下一首");
delay(500);
nextTrack();
break;
case DFPlayerCardRemoved:
sdErrorCount++;
Serial.printf("⚠️ SD卡錯誤 #%d\n", sdErrorCount);
if (sdErrorCount >= maxSDErrors) {
recoverSystem();
sdErrorCount = 0;
}
break;
case DFPlayerCardOnline:
Serial.println("✅ SD卡恢復");
sdErrorCount = 0;
if (!isPlaying && systemReady) {
delay(1000);
playCurrentTrack();
isPlaying = true;
}
break;
}
}
void recoverSystem() {
Serial.println("🔧 系統恢復中...");
isPlaying = false;
systemStatus = "恢復中";
delay(1000);
if (dfPlayerOnline) {
myDFPlayer.outputDevice(DFPLAYER_DEVICE_SD);
delay(2000);
startAutoRotation();
}
Serial.println("✅ 系統恢復完成");
}
// ==================== WiFi(translated)IFTTT ====================
void startWiFiConnection() {
Serial.println("📶 開始連接WiFi...");
systemStatus = "連接WiFi";
WiFi.begin(ssid, password);
}
void checkWiFi() {
static unsigned long lastCheck = 0;
static int wifiAttempts = 0;
if (millis() - lastCheck > 5000) {
if (WiFi.status() == WL_CONNECTED && !wifiConnected) {
wifiConnected = true;
Serial.println("✅ WiFi連接成功!");
Serial.printf(" IP: %s\n", WiFi.localIP().toString().c_str());
wifiAttempts = 0;
} else if (WiFi.status() != WL_CONNECTED && wifiConnected) {
wifiConnected = false;
Serial.println("📶 WiFi連線中斷");
} else if (WiFi.status() != WL_CONNECTED && !wifiConnected && wifiAttempts < 3) {
wifiAttempts++;
Serial.printf("📶 WiFi重試 %d/3\n", wifiAttempts);
WiFi.reconnect();
}
lastCheck = millis();
}
}
// ==================== WiFi(translated)IFTTT ====================
void sendIFTTT(String event, String value1, String value2) {
if (!wifiConnected || millis() - lastIFTTT < 180000) return;
HTTPClient http;
String url = iftttUrl;
http.begin(url);
http.addHeader("Content-Type", "application/json");
// 🔧 (translated):(translated)value1, value2(translated)value3(translated)
String concentratedData = value1;
if (value2.length() > 0) {
concentratedData += "|" + value2;
}
// (translated)MAX9814(translated)
concentratedData += "|當前音量:" + String(currentSoundLevel, 1) + "dB";
concentratedData += "|平均音量:" + String(avgSoundLevel, 1) + "dB";
concentratedData += "|峰值音量:" + String(peakSoundLevel, 1) + "dB";
concentratedData += "|ADC值:" + String(rawADCValue);
concentratedData += "|閾值:" + String(noiseThreshold) + "dB";
concentratedData += "|自動模式:" + String(autoMode ? "開啟" : "關閉");
concentratedData += "|系統音量:" + String(currentVolume);
concentratedData += "|當前歌曲:" + String(currentTrack);
concentratedData += "|播放狀態:" + String(isPlaying ? "播放中" : "暫停");
concentratedData += "|運行時間:" + String(millis()/1000) + "秒";
// 🔧 (translated):(translated)data(translated)value1, value2(translated)value3(translated)
String jsonData = "{\"value1\":\"" + concentratedData + "\",\"value2\":\"\",\"value3\":\"\"}";
int httpCode = http.POST(jsonData);
// updateIFTTT(translated)
unsigned long currentTime = millis() / 1000;
int hours = (currentTime / 3600) % 24;
int minutes = (currentTime / 60) % 60;
lastIFTTTTime = String(hours) + ":" + (minutes < 10 ? "0" : "") + String(minutes);
if (httpCode > 0) {
lastIFTTTSuccess = true;
lastIFTTTEvent = event;
iftttSuccessCount++;
Serial.printf("📤 IFTTT成功: %s (Code: %d)\n", event.c_str(), httpCode);
Serial.printf(" 數據: %s\n", concentratedData.c_str());
} else {
lastIFTTTSuccess = false;
iftttFailCount++;
Serial.printf("❌ IFTTT失敗: %s (Code: %d)\n", event.c_str(), httpCode);
}
http.end();
lastIFTTT = millis();
}
// ==================== OLEDinitialize ====================
void initOLED() {
Serial.println("📱 初始化OLED...");
if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println("❌ OLED初始化失敗");
oledOnline = false;
return;
}
oledOnline = true;
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
display.setCursor(0, 0);
display.println("ESP32 Audio System");
display.setCursor(0, 20);
display.println("MAX9814 + IFTTT");
display.setCursor(0, 40);
display.println("Initializing...");
display.display();
Serial.println("✅ OLED初始化成功");
}
// ==================== (translated) ====================
void handleCommands() {
if (!Serial.available()) return;
char cmd = Serial.read();
switch(cmd) {
case '1': case '2': case '3':
currentTrack = cmd - '0';
playCurrentTrack();
isPlaying = true;
break;
case '+':
if (currentVolume < 30) {
currentVolume++;
if (dfPlayerOnline) myDFPlayer.volume(currentVolume);
Serial.printf("🔊 音量: %d/30\n", currentVolume);
}
break;
case '-':
if (currentVolume > 0) {
currentVolume--;
if (dfPlayerOnline) myDFPlayer.volume(currentVolume);
Serial.printf("🔉 音量: %d/30\n", currentVolume);
}
break;
case 'p':
if (dfPlayerOnline) {
if (isPlaying) {
myDFPlayer.pause();
isPlaying = false;
systemStatus = "暫停";
Serial.println("⏸️ 暫停");
} else {
myDFPlayer.start();
isPlaying = true;
systemStatus = "播放中";
Serial.println("▶️ 繼續");
}
}
break;
case 's':
startAutoRotation();
Serial.println("🚀 重啟輪播");
break;
case 'n':
nextTrack();
break;
case 'a':
autoMode = !autoMode;
Serial.printf("🤖 自動模式: %s (閾值: %ddB)\n", autoMode ? "開啟" : "關閉", noiseThreshold);
sendIFTTT("auto_mode", autoMode ? "開啟" : "關閉", "閾值: " + String(noiseThreshold) + "dB");
break;
case 't':
sendIFTTT("test", "手動測試", "音量: " + String(currentSoundLevel, 1) + "dB");
Serial.println("📤 測試IFTTT");
break;
case 'v':
printVolumeInfo();
break;
case 'i':
printSystemStatus();
break;
case 'r':
recoverSystem();
Serial.println("🔧 手動重置系統");
break;
case 'g':
// (translated)MAX9814(translated)
static bool highGain = false;
highGain = !highGain;
digitalWrite(MIC_GAIN_PIN, highGain ? HIGH : LOW);
Serial.printf("🎚️ MAX9814增益: %s (%ddB)\n",
highGain ? "高" : "中",
highGain ? 60 : 50);
break;
case 'h':
printHelp();
break;
case 'm':
// (translated)
manualPageMode = !manualPageMode;
Serial.printf("📱 OLED翻頁: %s\n", manualPageMode ? "手動" : "自動");
break;
case '<':
// (translated)
if (manualPageMode) {
currentPage = (currentPage - 1 + TOTAL_PAGES) % TOTAL_PAGES;
Serial.printf("📱 切換到頁面: %d\n", currentPage + 1);
}
break;
case '>':
// (translated)
if (manualPageMode) {
currentPage = (currentPage + 1) % TOTAL_PAGES;
Serial.printf("📱 切換到頁面: %d\n", currentPage + 1);
}
break;
case 'u':
// (translated)threshold
if (noiseThreshold < 70) {
noiseThreshold += 5;
Serial.printf("🔊 噪音閾值: %ddB\n", noiseThreshold);
}
break;
case 'd':
// (translated)threshold
if (noiseThreshold > 20) {
noiseThreshold -= 5;
Serial.printf("🔉 噪音閾值: %ddB\n", noiseThreshold);
}
break;
case 'c':
// (translated)MAX9814
calibrateMAX9814();
break;
case 'z':
// resetvolume(translated)
resetVolumeStats();
break;
}
}
// ==================== (translated)display(translated) ====================
void printHelp() {
Serial.println("\n📋 指令說明:");
Serial.println("=====================================");
Serial.println("🎵 音樂控制:");
Serial.println(" 1/2/3 - 播放指定歌曲");
Serial.println(" p - 播放/暫停");
Serial.println(" s - 重啟自動輪播");
Serial.println(" n - 下一首");
Serial.println(" +/- - 音量調整");
Serial.println("");
Serial.println("🎤 音量檢測:");
Serial.println(" a - 切換自動播放模式");
Serial.println(" g - 切換MAX9814增益");
Serial.println(" u/d - 調整噪音閾值");
Serial.println(" v - 顯示音量資訊");
Serial.println(" c - 校準MAX9814");
Serial.println(" z - 重置音量統計");
Serial.println("");
Serial.println("📱 顯示控制:");
Serial.println(" m - 切換OLED翻頁模式");
Serial.println(" </> - 手動翻頁 (需開啟手動模式)");
Serial.println("");
Serial.println("🌐 系統控制:");
Serial.println(" t - 測試IFTTT");
Serial.println(" r - 重置系統");
Serial.println(" i - 系統狀態");
Serial.println(" h - 顯示此說明");
Serial.println("=====================================");
}
void printSystemStatus() {
Serial.println("\n📊 系統狀態:");
Serial.println("=====================================");
Serial.printf("🎵 音樂: Track %d/%d (%s)\n",
currentTrack, totalTracks,
isPlaying ? "播放中" : "暫停");
Serial.printf("🔊 音量: %d/30\n", currentVolume);
Serial.printf("⏱️ 運行時間: %lu 秒\n", millis() / 1000);
Serial.println("");
Serial.println("🔌 硬體狀態:");
Serial.printf(" DFPlayer: %s\n", dfPlayerOnline ? "✅ 正常" : "❌ 離線");
Serial.printf(" OLED: %s\n", oledOnline ? "✅ 正常" : "❌ 離線");
Serial.printf(" MAX9814: %s\n", max9814Online ? "✅ 正常" : "❌ 離線");
Serial.printf(" WiFi: %s", wifiConnected ? "✅ 已連接" : "❌ 未連接");
if (wifiConnected) {
Serial.printf(" (%s)", WiFi.localIP().toString().c_str());
}
Serial.println("");
Serial.println("");
Serial.println("🎤 音量檢測:");
Serial.printf(" 當前音量: %.1f dB\n", currentSoundLevel);
Serial.printf(" 平均音量: %.1f dB\n", avgSoundLevel);
Serial.printf(" 峰值音量: %.1f dB\n", peakSoundLevel);
Serial.printf(" 原始ADC: %d/4095\n", rawADCValue);
Serial.printf(" 自動模式: %s (閾值: %ddB)\n",
autoMode ? "開啟" : "關閉", noiseThreshold);
Serial.println("");
Serial.println("📱 OLED顯示:");
Serial.printf(" 當前頁面: %d/%d\n", currentPage + 1, TOTAL_PAGES);
Serial.printf(" 翻頁模式: %s\n", manualPageMode ? "手動" : "自動");
Serial.println("");
Serial.println("🌐 IFTTT狀態:");
Serial.printf(" 最後事件: %s\n",
lastIFTTTEvent.length() > 0 ? lastIFTTTEvent.c_str() : "無");
Serial.printf(" 狀態: %s\n", lastIFTTTSuccess ? "成功" : "失敗");
Serial.printf(" 成功次數: %d\n", iftttSuccessCount);
Serial.printf(" 失敗次數: %d\n", iftttFailCount);
Serial.printf(" 最後時間: %s\n", lastIFTTTTime.length() > 0 ? lastIFTTTTime.c_str() : "--:--");
Serial.println("");
Serial.printf("💾 記憶體: %d bytes 可用\n", ESP.getFreeHeap());
Serial.println("=====================================");
}
void printVolumeInfo() {
Serial.println("\n🎤 詳細音量資訊:");
Serial.println("=====================================");
Serial.printf("原始ADC值: %d (0-4095)\n", rawADCValue);
Serial.printf("電壓值: %.3f V\n", (rawADCValue / 4095.0) * 3.3);
Serial.printf("當前音量: %.1f dB\n", currentSoundLevel);
Serial.printf("平均音量: %.1f dB (過去%d個樣本)\n", avgSoundLevel, VOLUME_SAMPLES);
Serial.printf("峰值音量: %.1f dB\n", peakSoundLevel);
Serial.printf("噪音閾值: %d dB\n", noiseThreshold);
Serial.printf("緩衝區狀態: %s (%d/%d)\n",
volumeBufferFull ? "已滿" : "填充中",
bufferIndex, VOLUME_SAMPLES);
// display(translated)volumehistory
Serial.println("\n📈 最近音量歷史 (最新10個樣本):");
int startIdx = volumeBufferFull ? bufferIndex : 0;
int count = volumeBufferFull ? VOLUME_SAMPLES : bufferIndex;
int showCount = min(10, count);
for (int i = 0; i < showCount; i++) {
int idx = (startIdx - showCount + i + VOLUME_SAMPLES) % VOLUME_SAMPLES;
Serial.printf(" [%d] %.1f dB\n", i + 1, volumeBuffer[idx]);
}
Serial.println("=====================================");
}
// ==================== (translated) ====================
void resetVolumeStats() {
// resetvolume(translated)
for (int i = 0; i < VOLUME_SAMPLES; i++) {
volumeBuffer[i] = 0.0;
}
bufferIndex = 0;
volumeBufferFull = false;
currentSoundLevel = 0.0;
avgSoundLevel = 0.0;
peakSoundLevel = 0.0;
Serial.println("🔄 音量統計已重置");
}
void calibrateMAX9814() {
// MAX9814(translated)
Serial.println("🎚️ 開始MAX9814校準...");
Serial.println(" 請保持環境安靜 5 秒...");
float quietSum = 0.0;
int quietSamples = 0;
unsigned long startTime = millis();
while (millis() - startTime < 5000) {
int reading = analogRead(MIC_PIN);
float voltage = (reading / 4095.0) * 3.3;
float amplitude = abs(voltage - 1.65);
if (amplitude > 0.001) {
float dbValue = 20 * log10(amplitude / 0.001) + 20;
if (dbValue >= 0 && dbValue <= 80) {
quietSum += dbValue;
quietSamples++;
}
}
delay(100);
}
if (quietSamples > 0) {
float quietLevel = quietSum / quietSamples;
noiseThreshold = quietLevel + 10; // (translated) + 10dB
Serial.printf("✅ 校準完成!\n");
Serial.printf(" 安靜基準: %.1f dB\n", quietLevel);
Serial.printf(" 新閾值: %d dB\n", noiseThreshold);
sendIFTTT("calibration", "MAX9814校準完成", "新閾值: " + String(noiseThreshold) + "dB");
} else {
Serial.println("❌ 校準失敗,請檢查MAX9814連接");
}
}