iT邦幫忙

8

使用LINE發報機房溫度/濕度 過高通知

  • 分享至 

  • xImage
  •  

若有興趣土炮一個機房自動發報系統,可參考下方式,當機房過熱/過濕,會自動發送,商家(機房)頻道的LINE訊息。

簡易流程:

  1. 實體設備,成本應該不到 NTD:300 以下
  • a.DTH22感測器
  • b.Arduino nano 控制板
  • c.機房內的WINDOWS電腦 (原本有的)
  1. 安裝Arduino IDE 將code 寫入Arduino
  2. 申請 Line Developers 商家 (免費一個月200則訊息)
  3. 機房電腦安裝 Python 執行

一、購買零件材料
https://ithelp.ithome.com.tw/upload/images/20241230/20104753yyce7hrKOG.jpg
1.DHT22 模組 (三腳)
2.杜邦線 (母-母)
3. Arduino Nano 開發板 (已焊好針腳)
4.Mini USB 轉 USB TYPE A (此開發板的輸出為mini USB)


二、連接感測器 > Arduino > 電腦
https://ithelp.ithome.com.tw/upload/images/20241230/20104753rjhRTGHztb.png
1. 紅線 3.3V
2. 黑線接地
3. 使用 Arduino Nano D2 接收數位訊號


三、安裝 Arduino IDE
https://ithelp.ithome.com.tw/upload/images/20241230/20104753rTsGUg7qXd.png
1.需確定有驅動到連接埠 (相容nano開發板可能需安裝 CH340驅動)
2.選擇使用的開發板跟埠
3.範例電腦用NANO相容板與COM3 (依據實際變動)


四、安裝 Arduino 的 Libraries
https://ithelp.ithome.com.tw/upload/images/20241230/20104753nvftb7xbrz.png
1.工具 > 管理程式庫
2.輸入找 DTH sensor library 找到後安裝
3.輸入找 Adafruit DHT sensor library 找到後安裝


五、Arduino 運行程式
https://ithelp.ithome.com.tw/upload/images/20241230/20104753q3aMFUv5ip.png
1.複製下方程式碼 到 Arduino IDE
2.檢查後上傳
3.若上傳出現錯誤,請改選擇一下所使用的開發板晶片
4.開啟連接埠監測視窗

#include <DHT.h>

// 定義 DHT22 感測器型號與連接腳位
#define DHTPIN 2      // DHT22 的數據腳接 Arduino 的 D2 腳位
#define DHTTYPE DHT22 // 感測器型號為 DHT22

// 初始化 DHT22 感測器
DHT dht(DHTPIN, DHTTYPE);

void setup() {
  // 設定串列埠速率為 9600
  Serial.begin(9600);
  
  // 啟動 DHT22 感測器
  dht.begin();
}

void loop() {
  // 延遲以確保感測器穩定 (至少 2 秒)
  delay(2000);

  // 讀取溫度與濕度
  float temperature = dht.readTemperature(); // 攝氏溫度
  float humidity = dht.readHumidity();       // 濕度百分比

  // 確認讀取是否成功
  if (isnan(temperature) || isnan(humidity)) {
    return;
  }

  // 只輸出溫度與濕度數值
  Serial.print(temperature);
  Serial.print(",");
  Serial.println(humidity);
}

六、測試連接埠監測視窗是否正常回傳
https://ithelp.ithome.com.tw/upload/images/20241230/20104753kKhPzlmJEi.png
1.有回傳 溫度 與 濕度數值
2.關閉 Arduino IDE


七、Line Developers 註冊後要加入成為商家 (才會有 200則/月 免費訊息)
Line Developers 網址連結
建立頻道,使用 Messaging API
https://ithelp.ithome.com.tw/upload/images/20241230/20104753YllSVghMMp.png
建立供應商
https://ithelp.ithome.com.tw/upload/images/20241230/20104753jFGwsAyEu9.png

1.建好頻道後 在 Basic settings 分頁可以看到 Your user ID: 『記下:等等PY程式要填入』
2.在 Messaging API 分頁可以看到 Channel access token (long-lived) 『記下:等等PY程式要填入』
3.其他用QR CODE加入機房群的同仁 user ID 必須過 webhook.site 監聽 LINE user ID
在 Messaging API 分頁可以加入你的 webhook.site 透過訊息傳送記錄,抓取 LINE user ID
關於LINE USER ID-1
關於LINE USER ID-2
關於Webhook.site

https://ithelp.ithome.com.tw/upload/images/20241230/20104753DtZq7dtgBQ.png


八、機房電腦安裝 Python (不想安裝也可以透過打包成exe,在機房電腦上執行)
GUI畫面如下,執行若出現錯誤則可能需加裝缺少python程式庫。

https://ithelp.ithome.com.tw/upload/images/20241230/20104753fg9sZGGzIM.png

  1. 下載安裝 https://www.python.org/downloads/
  2. 在 C:\Temperature 建立資料夾,並用記事本存一個副檔名為PY的文件檔,例如TemperatureGO.PY
  3. PY檔案貼上以下程式碼
  • a. 要修改程式內 "輸入自己申請的LINEACCESS_TOKEN" 跟 "輸入傳送LINE user ID1~N"
  • b. 機房溫度這邊是溫度 30度發報 濕度80%發報 發報間隔10分鐘 (免費只有200則/月)
  • c. 路徑預設為"C:\Temperature"
  • d. 每一天溫/溼度變化會存一個日期.TXT檔案可以用EXCEL回看
  • e. 圖表內X軸 3600點,若每2秒讀取一點,只會顯示約2小時內的數據變化
  1. 確認 感測器與控制板透過USB線接上機房電腦
  2. 執行 TemperatureGO.PY
import tkinter as tk
from tkinter import ttk, messagebox
import serial
import serial.tools.list_ports
import threading
import datetime
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import os
import requests
import time
import os

# 設置目錄
base_path = "C:\\Temperature"

# 檢查目錄是否存在,如果不存在則創建
if not os.path.exists(base_path):
    os.makedirs(base_path)

# 設置工作目錄
os.chdir(base_path)
print("目前工作目錄:", os.getcwd())


# LINE Messaging API 設定
LINE_CHANNEL_ACCESS_TOKEN = "輸入自己申請的LINEACCESS_TOKEN"

user_ids = [
    "輸入傳送LINE  user ID1",
    "輸入傳送LINE  user ID2",
    "輸入傳送LINE  user ID3",
    "輸入傳送LINE  user ID4"
]

# 初始化全域變數
serial_port = None
data_lock = threading.Lock()
data_points = []
is_reading = False
last_alert_time = 0  # 追蹤最後一次發送LINE通知的時間

# GUI 設定
root = tk.Tk()
root.title("串列埠監控工具")
root.option_add("*Font", "max.ttf")

# 實時數據顯示
current_temp = tk.StringVar(value="N/A")
current_humid = tk.StringVar(value="N/A")

# 創建圖表
fig, ax1 = plt.subplots()
ax2 = ax1.twinx()
ax1.set_xlabel("TIME ( 3600 POINT )")
ax1.set_ylabel("Temperature (°C)", color="red")
ax2.set_ylabel("Humidity (%)", color="blue")
lines_temp, = ax1.plot([], [], "r-", label="Temperature")
lines_humid, = ax2.plot([], [], "b-", label="Humidity")

# 添加畫布到 GUI
canvas = FigureCanvasTkAgg(fig, master=root)
canvas_widget = canvas.get_tk_widget()
canvas_widget.grid(row=5, column=0, columnspan=3, padx=5, pady=5)

# 幫助函式
def scan_ports():
    """掃描可用的COM埠"""
    ports = serial.tools.list_ports.comports()
    return [port.device for port in ports]

def connect():
    """連接串列埠"""
    global serial_port, is_reading
    com = com_combobox.get()
    baudrate = baudrate_combobox.get()

    if not com or not baudrate:
        messagebox.showerror("錯誤", "請選擇COM埠與交涉率")
        return

    try:
        serial_port = serial.Serial(com, int(baudrate), timeout=1)
        connect_button.config(state=tk.DISABLED)
        disconnect_button.config(state=tk.NORMAL)
        status_label.config(text=f"已連接到 {com} @ {baudrate}bps", fg="green")
        is_reading = True
        threading.Thread(target=read_serial, daemon=True).start()
    except Exception as e:
        messagebox.showerror("錯誤", f"無法連接:{e}")

def disconnect():
    """斷開串列埠"""
    global serial_port, is_reading
    if serial_port and serial_port.is_open:
        serial_port.close()
    serial_port = None
    is_reading = False
    connect_button.config(state=tk.NORMAL)
    disconnect_button.config(state=tk.DISABLED)
    status_label.config(text="已斷開連接", fg="red")

def read_serial():
    """讀取串列埠資料"""
    global serial_port, data_points, is_reading, last_alert_time
    while is_reading and serial_port and serial_port.is_open:
        try:
            line = serial_port.readline().decode().strip()
            if line:
                temp, humid = map(float, line.split(","))
                current_temp.set(f"{temp:.1f} °C")
                current_humid.set(f"{humid:.1f} %")
                record_data(temp, humid)

                with data_lock:
                    data_points.append((temp, humid))
                    if len(data_points) > 3600:  # 修改為3600個點
                        data_points.pop(0)
                update_plot()

                # 檢查過高溫度或濕度,並間隔10分鐘才發送一次LINE
                current_time = time.time()
                if (temp > 30 or humid > 80) and (current_time - last_alert_time > 600):  # 600秒 = 10分鐘
                    send_line_alert(temp, humid)
                    last_alert_time = current_time
        except Exception as e:
            print(f"讀取錯誤:{e}")
            break

def record_data(temp, humid):
    """紀錄數據到每日檔案"""
    date_str = datetime.date.today().strftime("%Y-%m-%d")
    filename = f"{date_str}.txt"
    timestamp = datetime.datetime.now().strftime("%H:%M:%S")
    with open(filename, "a") as f:
        f.write(f"{timestamp}\t{temp}\t{humid}\n")

def update_plot():
    """更新圖表"""
    with data_lock:
        temps = [point[0] for point in data_points]
        humids = [point[1] for point in data_points]
        times = list(range(len(data_points)))

    lines_temp.set_data(times, temps)
    lines_humid.set_data(times, humids)
    ax1.set_xlim(max(0, len(times) - 3600), len(times))  # X軸顯示最多3600個點
    ax1.set_ylim(min(temps, default=0) - 5, max(temps, default=40) + 5)
    ax2.set_ylim(min(humids, default=0) - 5, max(humids, default=100) + 5)
    canvas.draw()

def send_line_alert(temp, humid):
    """發送LINE通知"""
    message = f"警告!溫度: {temp:.1f}°C, 濕度: {humid:.1f}%"
    headers = {
        "Authorization": f"Bearer {LINE_CHANNEL_ACCESS_TOKEN}",
        "Content-Type": "application/json"
    }
    for user_id in user_ids:
        payload = {
            "to": user_id,
            "messages": [{"type": "text", "text": message}]
        }
        try:
            response = requests.post("https://api.line.me/v2/bot/message/push", headers=headers, json=payload)
            if response.status_code != 200:
                print(f"LINE 通知錯誤: {response.status_code}, {response.text}")
        except Exception as e:
            print(f"LINE 通知錯誤: {e}")

# GUI 元素
com_label = tk.Label(root, text="COM埠:")
com_label.grid(row=0, column=0, padx=5, pady=5, sticky=tk.W)
com_combobox = ttk.Combobox(root, values=scan_ports(), state="readonly")
com_combobox.grid(row=0, column=1, padx=5, pady=5)

baudrate_label = tk.Label(root, text="交涉率:")
baudrate_label.grid(row=1, column=0, padx=5, pady=5, sticky=tk.W)
baudrate_combobox = ttk.Combobox(root, values=["9600", "115200"], state="readonly")
baudrate_combobox.grid(row=1, column=1, padx=5, pady=5)
baudrate_combobox.set("9600")

connect_button = tk.Button(root, text="連接", command=connect)
connect_button.grid(row=2, column=0, padx=5, pady=5)

disconnect_button = tk.Button(root, text="斷開", command=disconnect, state=tk.DISABLED)
disconnect_button.grid(row=2, column=1, padx=5, pady=5)

status_label = tk.Label(root, text="未連接", fg="red")
status_label.grid(row=3, column=0, columnspan=2, pady=5)

refresh_button = tk.Button(root, text="刷新COM埠", command=lambda: com_combobox.config(values=scan_ports()))
refresh_button.grid(row=0, column=2, padx=5, pady=5)

real_time_label_temp = tk.Label(root, textvariable=current_temp, fg="red")
real_time_label_temp.grid(row=4, column=0, padx=5, pady=5)

real_time_label_humid = tk.Label(root, textvariable=current_humid, fg="blue")
real_time_label_humid.grid(row=4, column=1, padx=5, pady=5)

# 開始 GUI 事件循環
root.mainloop()


圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

0
kawa0710
iT邦研究生 5 級 ‧ 2024-12-31 08:45:33

完整教學已讚已收藏

我要留言

立即登入留言