這份統合版將你原本的「地震學學習回顧互動網頁」與「Raspberry Pi 微型地震儀成果展示網頁」整合成雙版面網站: 版面一負責說明地震學概念、野外觀測與案例分析;版面二負責展示 Raspberry Pi、MPU6050、LED、蜂鳴器、Discord Webhook 與成果影片。
用震波走時、折射、反射、震源機制與嘉義大埔案例,說明「地震訊號如何被觀測與解釋」。
用 Raspberry Pi 加上 MPU6050 感測加速度,透過 LED、蜂鳴器與 Discord 呈現預警系統成果。
此版面嵌入你上傳的地震學互動網頁,保留原本的學習地圖、章節核心、SmartSolo、嘉義大埔案例、反思與測驗。
此版面嵌入你前面製作的 Raspberry Pi 成果展示網頁,保留主程式、接線教學、門檻模擬、成果影片、預期成果、製作困難與解決辦法。
import smbus
import time
import math
import requests
from datetime import datetime
import RPi.GPIO as GPIO
# ==============================================================================
# 核心參數設定區
# ==============================================================================
# ⚠️ 請將下方字串替換為你測試成功的 Discord Webhook 網址(若無請保持原樣)
DISCORD_WEBHOOK_URL = "在這裡貼上你的_Discord_Webhook_網址"
# 三級震動門檻設定 (單位: g)
THRESHOLD_L1 = 1.0
THRESHOLD_L2 = 1.5
THRESHOLD_L3 = 2.0
COOLDOWN_TIME = 7 # 警報冷卻時間 (單位: 秒)
LED_PIN = 18 # 使用 GPIO 18 (支援硬體 PWM)
BUZZER_PIN = 24 # 蜂鳴器使用 GPIO 24
# ==============================================================================
# 硬體初始化
# ==============================================================================
GPIO.setmode(GPIO.BCM)
# 1. LED 初始化
GPIO.setup(LED_PIN, GPIO.OUT)
led_pwm = GPIO.PWM(LED_PIN, 1000)
led_pwm.start(0)
# 2. 蜂鳴器初始化
GPIO.setup(BUZZER_PIN, GPIO.OUT)
buzzer_pwm = GPIO.PWM(BUZZER_PIN, 2000)
buzzer_pwm.start(0)
# 3. MPU6050 I2C 初始化
Device_Address = 0x68
PWR_MGMT_1 = 0x6B
ACCEL_XOUT_H = 0x3B
bus = smbus.SMBus(1)
try:
bus.write_byte_data(Device_Address, PWR_MGMT_1, 1) # 喚醒感測器
print("\033[92m[系統] ✅ MPU6050 硬體初始化成功。\033[0m")
except OSError:
print("\033[91m[錯誤] ❌ 無法連線至 MPU6050,請檢查線路是否插緊。\033[0m")
GPIO.cleanup()
exit()
# ==============================================================================
# 功能函式定義
# ==============================================================================
def read_raw_data(addr):
high = bus.read_byte_data(Device_Address, addr)
low = bus.read_byte_data(Device_Address, addr+1)
value = ((high << 8) | low)
if value > 32768:
value = value - 65536
return value
def send_discord_alert(accel_val, level_title):
if "在這裡貼上你的" in DISCORD_WEBHOOK_URL or not DISCORD_WEBHOOK_URL.startswith("http"):
return
current_time_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
payload = {
"content": f"🚨 **【地震觀測站警報發布】** 🚨\n"
f"📢 **警報等級:** {level_title}\n"
f"📅 **觸發時間:** `{current_time_str}`\n"
f"📈 **最大合成加速度:** **{accel_val:.3f} g**\n"
f"💡 *現場警報器與 LED 燈組已同步全力運作中!*"
}
try:
requests.post(DISCORD_WEBHOOK_URL, json=payload, timeout=5)
except Exception:
pass # 忽略網路報錯,確保現場運作順暢
# ==============================================================================
# 主迴圈監測邏輯
# ==============================================================================
last_alert_time = 0
max_pga_record = 0.0 # ✨ 優化四:紀錄本次實驗啟動以來的「地表最大加速度」
print("\n=======================================================")
print(" 🌋 微型地震預警與環境監測系統 (成果發表終極版) 🌋 ")
print("=======================================================")
print("正在即時採樣中... (欲停止監測或重設請按 Ctrl+C)\n")
try:
while True:
try:
# 1. 讀取三個軸向的原始資料
acc_x = read_raw_data(ACCEL_XOUT_H) / 16384.0
acc_y = read_raw_data(ACCEL_XOUT_H + 2) / 16384.0
acc_z = read_raw_data(ACCEL_XOUT_H + 4) / 16384.0
except OSError:
print("⚠️ [警告] 訊號瞬間中斷,正在自動防護重試...", end='\r')
time.sleep(0.05)
continue
# 2. 計算三軸合成空間向量大小
total_accel = math.sqrt(acc_x**2 + acc_y**2 + acc_z**2)
# ✨ 優化四:如果突破歷史最高紀錄,就地更新!
if total_accel > max_pga_record:
max_pga_record = total_accel
# 3. 判斷當前震動等級與對應的「特效參數」
current_level = 0
level_text = ""
flash_period = 1.0
buzzer_freq = 1000
led_brightness = 0
# ✨ 優化三:利用時間正弦波,為低階地震打造優雅的呼吸燈特效
# time.time() * 6 代表速度,透過 sin 讓數值在 0 ~ 1 之間來回起伏
breathing_brightness = int((math.sin(time.time() * 6) + 1) / 2 * 20) # 最高 20% 安全亮度
if total_accel >= THRESHOLD_L3:
current_level = 3
level_text = "🔴【三級:強烈有感地震】"
flash_period = 0.15 # 爆閃
# ✨ 優化二:三級地震採用「空襲警報式」高頻連續高音
buzzer_freq = 2500 if (int(time.time() * 10) % 2 == 0) else 2000
led_brightness = 40 # 提高亮度以彰顯災情
elif total_accel >= THRESHOLD_L2:
current_level = 2
level_text = "🟠【二級:中度有感地震】"
flash_period = 0.4
# ✨ 優化二:二級地震採用「救護車式」雙音頻交替切換 (逼~啵~)
buzzer_freq = 800 if (int(time.time() * 4) % 2 == 0) else 1300
led_brightness = 20
elif total_accel >= THRESHOLD_L1:
current_level = 1
level_text = "🟡【一級:輕微有感地震】"
flash_period = 1.0
buzzer_freq = 440 # 溫和低沉單音
led_brightness = breathing_brightness # 啟用呼吸燈特效
# ✨ 優化一:終端機動態進度條美化與色彩系統
# 以 3.5g 為滿格 (35 格) 進行比例計算
bar_length = min(int(total_accel * 10), 35)
bar = "█" * bar_length + "░" * (35 - bar_length)
if current_level == 3:
color_code = "\033[91m" # 搶眼紅
elif current_level == 2:
color_code = "\033[93m" # 警告橘黃
elif current_level == 1:
color_code = "\033[96m" # 水藍綠
else:
color_code = "\033[92m" # 安全綠
# 終端機同一行不斷刷新,同時顯示動態能量條與右側的「歷史最大紀錄保持」
print(f"{color_code}[{bar}] 即時震度: {total_accel:.2f}g (L{current_level}) \033[0m | 🏆 \033[1;35m最高 PGA 紀錄: {max_pga_record:.2f} g\033[0m", end='\r')
# 4. 執行硬體反應與報警
if current_level > 0:
# 針對二級與三級,實施週期性的閃爍與鳴叫
if current_level in [2, 3]:
if (time.time() % flash_period) < (flash_period / 2):
led_pwm.ChangeDutyCycle(led_brightness)
buzzer_pwm.ChangeFrequency(buzzer_freq)
buzzer_pwm.ChangeDutyCycle(30) # 固定 30% 電流輸出避免主動式蜂鳴器破音
else:
led_pwm.ChangeDutyCycle(0)
buzzer_pwm.ChangeDutyCycle(0)
else:
# 一級地震直接輸出平滑的呼吸燈,並維持單音鳴叫
led_pwm.ChangeDutyCycle(led_brightness)
buzzer_pwm.ChangeFrequency(buzzer_freq)
buzzer_pwm.ChangeDutyCycle(10) # 輕微提示音量
# Discord 雲端推播 (含冷卻機制)
current_timestamp = time.time()
if current_timestamp - last_alert_time > COOLDOWN_TIME:
# 在畫面上印出乾淨漂亮的警報發布觸發通知
print(f"\n\033[1;33m🔥 [觀測站事件發布] {level_text}!數值: {total_accel:.2f} g\033[0m")
send_discord_alert(total_accel, level_text)
last_alert_time = current_timestamp
else:
# 安全無事狀態,全部歸零
led_pwm.ChangeDutyCycle(0)
buzzer_pwm.ChangeDutyCycle(0)
time.sleep(0.04) # 稍微提高採樣率至每秒約 25 次,抓取晃動更靈敏
except KeyboardInterrupt:
print("\n\n\033[95m[系統] 收到終止指令,正在安全下線觀測站...")
led_pwm.stop()
buzzer_pwm.stop()
GPIO.cleanup()
print("[系統] 所有硬體資源已安全釋放。最大 PGA 歷史紀錄為: {:.2f} g。再見!\033[0m".format(max_pga_record))