ตัวอย่าง Code Machine Condition Monitoring & Anomaly Detection โดยใช้บอร์ด ESP32

#include <WiFi.h>

#include <HTTPClient.h>

#include <OneWire.h>

#include <DallasTemperature.h>

#include <Wire.h>

#include <Adafruit_Sensor.h>

#include <Adafruit_ADXL345_U.h>

#include <math.h>

// ==================== CONFIGURATION ====================

const char* WIFI_SSID = “YOUR_WIFI_SSID”;

const char* WIFI_PASS = “YOUR_WIFI_PASSWORD”;

const char* GOOGLE_SCRIPT_URL = “YOUR_GOOGLE_APPS_SCRIPT_WEB_APP_URL”;

const char* MACHINE_NAME = “MACHINE_01”; // ชื่อเครื่องจักร

// — Hardware Pins —

#define SCT_PIN 34          // ADC Pin สำหรับ SCT-013 (ผ่าน DC Bias Circuit)

#define ONE_WIRE_BUS 4      // DS18B20 Data Pin

// — Limits & Ratio Configurations —

// หมายเหตุ: สามารถนำตัวแปรเหล่านี้ไปเชื่อมต่อกับ WiFiManager ในอนาคตได้

float tempCriticalLimit    = 75.0;  // ลิมิตอุณหภูมิระดับ CRITICAL (°C)

float currentCriticalLimit = 50.0;  // ลิมิตกระแสระดับ CRITICAL (A)

const float TEMP_WARN_LIMIT   = 60.0;  // ลิมิตอุณหภูมิระดับ WARNING (°C)

const float ANOMALY_THRESHOLD = 3.0;   // ค่า Z-Score พื้นฐาน

const float Z_CRITICAL_RATIO  = 1.5;   // ตัวคูณสำหรับ Critical Z-Score (3.0 * 1.5 = 4.5)

const float Z_WARN_RATIO      = 1.0;   // ตัวคูณสำหรับ Warning Z-Score  (3.0 * 1.0 = 3.0)

// — Self-Learning Calibration —

const int LEARN_SAMPLES = 100;        // จำนวนตัวอย่างสุ่มอ่านเพื่อเรียนรู้ Baseline

// ==================== OBJECTS & GLOBAL VARIABLES ====================

OneWire oneWire(ONE_WIRE_BUS);

DallasTemperature tempSensor(&oneWire);

Adafruit_ADXL345_Unified accel = Adafruit_ADXL345_Unified(12345);

float vib_baseline_mean = 0.0; // ค่าเฉลี่ยแรงสั่นสะเทือนสภาวะปกติ (Mean)

float vib_baseline_std  = 0.0; // ค่าเบี่ยงเบนมาตรฐานปกติ (StdDev)

// ==================== FUNCTIONS ====================

// 1. ระบบเรียนรู้ค่าสั่นสะเทือนพื้นฐานเมื่อเปิดเครื่อง (Self-Learning)

void learnVibrationBaseline() {

  Serial.println(“\n[LEARNING] Starting vibration baseline calibration…”);

  Serial.println(“[LEARNING] Ensure the machine is running in baseline state…”);

  float values[LEARN_SAMPLES];

  float sum = 0.0;

  for (int i = 0; i < LEARN_SAMPLES; i++) {

    sensors_event_t event;

    accel.getEvent(&event);

    // คำนวณ Vector Magnitude: sqrt(x^2 + y^2 + z^2)

    float mag = sqrt(pow(event.acceleration.x, 2) +

                     pow(event.acceleration.y, 2) +

                     pow(event.acceleration.z, 2));

    values[i] = mag;

    sum += mag;

    delay(50);

  }

  // คำนวณค่าเฉลี่ย (Mean)

  vib_baseline_mean = sum / LEARN_SAMPLES;

  // คำนวณส่วนเบี่ยงเบนมาตรฐาน (Standard Deviation)

  float variance_sum = 0.0;

  for (int i = 0; i < LEARN_SAMPLES; i++) {

    variance_sum += pow(values[i] – vib_baseline_mean, 2);

  }

  vib_baseline_std = sqrt(variance_sum / LEARN_SAMPLES);

  // ป้องกันกรณีป้อนค่า 0 เพื่อหลีกเลี่ยงปัญหา Division by Zero

  if (vib_baseline_std < 0.001) vib_baseline_std = 0.001;

  Serial.printf(“[LEARNING] Complete! Baseline Mean: %.3f m/s², StdDev: %.3f m/s²\n\n”,

                vib_baseline_mean, vib_baseline_std);

}

// 2. อ่านค่ากระแสสมบูรณ์ (SCT-013 60A/1V)

float readCurrentRMS() {

  const int SAMPLES = 300;

  float sum_sq = 0.0;

  for (int i = 0; i < SAMPLES; i++) {

    int raw = analogRead(SCT_PIN);

    float voltage = ((raw – 2048) / 4095.0) * 3.3; // ลบ Offset Center (~1.65V)

    sum_sq += voltage * voltage;

    delayMicroseconds(500);

  }

  float v_rms = sqrt(sum_sq / SAMPLES);

  float current_rms = v_rms * 60.0; // อัตราส่วน SCT-013 60A/1V (60A ต่อ 1V RMS)

  if (current_rms < 0.2) current_rms = 0.0; // Noise Filter

  return current_rms;

}

// 3. อ่านค่าแรงสั่นสะเทือน และคำนวณ Z-Score

void readVibrationAndZScore(float &mag, float &z_score) {

  sensors_event_t event;

  accel.getEvent(&event);

  mag = sqrt(pow(event.acceleration.x, 2) +

             pow(event.acceleration.y, 2) +

             pow(event.acceleration.z, 2));

  // Z-Score = (X – Mean) / StdDev

  z_score = (mag – vib_baseline_mean) / vib_baseline_std;

}

// 4. ส่งข้อมูล JSON ไปยัง Google Apps Script (doPost)

void sendToGoogleScript(float temp, float vib, float current, float z_score, String status) {

  if (WiFi.status() != WL_CONNECTED) {

    Serial.println(“[WIFI] Disconnected, skipping HTTP POST…”);

    return;

  }

  HTTPClient http;

  http.begin(GOOGLE_SCRIPT_URL);

  http.addHeader(“Content-Type”, “application/json”);

  http.setFollowRedirects(HTTPC_STRICT_FOLLOW_REDIRECTS); // รองรับ HTTP 302 Redirection ของ Google Web App

  // JSON Payload ให้ตรงกับโครงสร้างที่ doPost(e) รอรับ

  String jsonPayload = “{“;

  jsonPayload += “\”machine_name\”:\”” + String(MACHINE_NAME) + “\”,”;

  jsonPayload += “\”temperature\”:” + String(temp, 2) + “,”;

  jsonPayload += “\”vibration\”:” + String(vib, 3) + “,”;

  jsonPayload += “\”current\”:” + String(current, 2) + “,”;

  jsonPayload += “\”z_score\”:” + String(z_score, 2) + “,”;

  jsonPayload += “\”status\”:\”” + status + “\””;

  jsonPayload += “}”;

  Serial.println(“[HTTP] Sending JSON: ” + jsonPayload);

  int httpCode = http.POST(jsonPayload);

  if (httpCode > 0) {

    String response = http.getString();

    Serial.printf(“[HTTP] Response Code: %d, Body: %s\n”, httpCode, response.c_str());

  } else {

    Serial.printf(“[HTTP] POST Failed, error: %s\n”, http.errorToString(httpCode).c_str());

  }

  http.end();

}

// ==================== SETUP & LOOP ====================

void setup() {

  Serial.begin(115200);

  // 1. เชื่อมต่อ WiFi

  WiFi.begin(WIFI_SSID, WIFI_PASS);

  Serial.print(“Connecting to WiFi”);

  while (WiFi.status() != WL_CONNECTED) {

    delay(500);

    Serial.print(“.”);

  }

  Serial.println(“\nWiFi Connected!”);

  // 2. เริ่มต้นเซนเซอร์

  tempSensor.begin();

  if (!accel.begin()) {

    Serial.println(“ERROR: ADXL345 not found, check wiring!”);

    while (1);

  }

  accel.setRange(ADXL345_RANGE_16G);

  // 3. เริ่มกระบวนการ Self-Learning Baseline Vibration

  learnVibrationBaseline();

}

void loop() {

  // — 1. อ่านค่าเซนเซอร์ทุกตัว —

  float currentRMS = readCurrentRMS();

  tempSensor.requestTemperatures();

  float currentTemp = tempSensor.getTempCByIndex(0);

  float currentVib = 0.0;

  float zScore = 0.0;

  readVibrationAndZScore(currentVib, zScore);

  // — 2. ประเมินสถานะ 3 ระดับ (NORMAL / WARNING / CRITICAL) —

  String statusStr = “NORMAL”;

  bool isAnomaly = false;

  // ตรวจจับเงื่อนไข CRITICAL โดยเทียบกับค่า Limit Dynamic

  if (currentTemp >= tempCriticalLimit ||

      currentRMS >= currentCriticalLimit ||

      zScore >= (ANOMALY_THRESHOLD * Z_CRITICAL_RATIO)) {

    // 🔴 CRITICAL: สภาวะวิกฤต/ผิดปกติ

    statusStr = “CRITICAL”;

    isAnomaly = true;

  }

  else if (currentTemp >= TEMP_WARN_LIMIT || zScore >= (ANOMALY_THRESHOLD * Z_WARN_RATIO)) {

    // 🟡 WARNING: เฝ้าระวัง (เริ่มเบี่ยงเบนจาก Baseline)

    statusStr = “WARNING”;

    isAnomaly = false; // ยังไม่เปิดไฟเตือนกระพริบ

  }

  else {

    // 🟢 NORMAL: เครื่องจักรทำงานสมบูรณ์ปกติ

    statusStr = “NORMAL”;

    isAnomaly = false;

  }

  // พิมพ์ Log แสดงผลการประเมิน

  Serial.printf(“[%s] Temp: %.2f °C | Curr: %.2f A | Vib: %.3f m/s² | Z-Score: %.2f | Status: %s (Anomaly: %s)\n”,

                MACHINE_NAME, currentTemp, currentRMS, currentVib, zScore, statusStr.c_str(), isAnomaly ? “TRUE” : “FALSE”);

  // — 3. ส่งเข้า Google Apps Script —

  sendToGoogleScript(currentTemp, currentVib, currentRMS, zScore, statusStr);

  delay(5000); // ส่งข้อมูลทุกๆ 5 วินาที

}

ระบบเฝ้าระวังและตรวจจับความผิดปกติของเครื่องจักรในอุตสาหกรรม (Machine Condition Monitoring & Anomaly Detection) โดยใช้บอร์ด ESP32 ทำงานร่วมกับเซนเซอร์ 3 ชนิด และส่งข้อมูลขึ้น Google Sheets / Google Apps Script เพื่อนำไปทำ Dashboard หรือระบบแจ้งเตือนต่อไป

เราใช้คุกกี้เพื่อพัฒนาประสิทธิภาพ และประสบการณ์ที่ดีในการใช้เว็บไซต์ของคุณ คุณสามารถศึกษารายละเอียดได้ที่ นโยบายความเป็นส่วนตัว และสามารถจัดการความเป็นส่วนตัวเองได้ของคุณได้เองโดยคลิกที่ ตั้งค่า

ตั้งค่าความเป็นส่วนตัว

คุณสามารถเลือกการตั้งค่าคุกกี้โดยเปิด/ปิด คุกกี้ในแต่ละประเภทได้ตามความต้องการ ยกเว้น คุกกี้ที่จำเป็น

ยอมรับทั้งหมด
จัดการความเป็นส่วนตัว
  • เปิดใช้งานตลอด

บันทึกการตั้งค่า