from machine import I2C, Pin, PWM #مكتبة (machine) عشان اعرف ال (Pin )وال (I2C)
import time #عشان اتحكم في الوقت والتاخير
import math #عشان احسب الجذر التربيعي لمحصلة التسارع
# هحدد ارقام ال Pins.......
# بنستخدم Pin(0, Pin.OUT) عشان نحدد رقم الطرف ونوعه (مخرج)
# وما ينفعش نكتب الرقم مباشرة (زي: buzzer = 0)؛
# لأن الميكروكنترولر محتاج يفهم إن ده طرف هاردوير حقيقي هنبعت له كهرباء (Value)،
# مش مجرد رقم حسابي عادي، ولو كتبناه رقم بس الكود هيدي إيرور ومش هيشغل الجهاز.
buzzer = Pin(0, Pin.OUT)
buzzer = PWM(Pin(0))
BUZZER_FREQ = 500
buzzer.freq(BUZZER_FREQ)
buzzer.duty_u16(0)
red_led = Pin(1, Pin.OUT)
green_led = Pin(2, Pin.OUT)
reset_btn = Pin(3, Pin.IN, Pin.PULL_UP) # زرار إعادة التشغيل (Reset) متوصل على الطرف 3، وده طرف دخل (Input) مع مقاومة سحب لأعلى (Pull-Up)
#اعداد ال I2C للشاشة والحساس
i2c = I2C(1, sda=Pin(26), scl=Pin(27), freq=100000) #تحديد رقم ال I2C و ارقام ال Pins و سرعة النقل
# تحديدالمعايير وقيم الوقت
IMPACT_THRESHOLD = 2 # حد التسارع للتحقق اذا كان الجسم تعرض لاصطدام او لأ
LOW_MOVEMENT_MIN = 0.5 #الحد الادنى للسكون بعد الاصطدام
LOW_MOVEMENT_MAX = 1.6 #الحد الاقصى للسكون بعد الاصطدام
TIME_WINDOW_SEC = 4 #المدة المسموح فيها التأكيد من السكون بعد الاصطدام
LOW_MOVEMENT_DURATION = 1 #مدة السكون المطلوبة لتأكيد السقوط
# إعداد الحساس (MPU6050) والشاشة (LCD)
MPU_ADDR = 0x68 # العنوان الخاص بحساس التسارع
# تشغيل الحساس بإلغاء وضع النوم (Sleep Mode) مع استخدام try/except لمنع إيقاف الكود لو الحساس غير متصل
try:
i2c.writeto_mem(MPU_ADDR, 0x6B, b'\x00')
except:
pass
# دالة رياضية لحساب التسارع الكلي (G-Force) من محور X, Y, Z باستخدام فيثاغورس
def get_accel():
try:
data = i2c.readfrom_mem(MPU_ADDR, 0x3B, 6)
x = (int.from_bytes(data[0:2], 'big', True)) / 16384.0
y = (int.from_bytes(data[2:4], 'big', True)) / 16384.0
z = (int.from_bytes(data[4:6], 'big', True)) / 16384.0
return math.sqrt(x**2 + y**2 + z**2)
except:
return 1.0 # قيمة افتراضية لو حدث خطأ في القراءة
# كلاس مبسط للتحكم بشاشة الـ LCD عبر الـ I2C
class SimpleLCD:
def __init__(self, i2c):
self.i2c = i2c
time.sleep_ms(50) # انتظار 20 مللي ثانية قبل ارسال اي بيانات او اوامر من الراسبيري باي الى الشاشة
# أوامر التهيئة القياسية لتشغيل الشاشة
for cmd in [0x33, 0x32, 0x28, 0x0C, 0x06, 0x01]:
self.send(cmd, 0)
time.sleep_ms(5) # انتظار 5 ميلي ثانية بين إرسال الأوامر للشاشة لضمان معالجتها بشكل صحيح
def send(self, val, mode):
# دالة داخلية لتقسيم البيانات وإرسالها عبر بروتوكول الـ I2C للشاشة
high = mode | (val & 0xF0) | 0x08
low = mode | ((val << 4) & 0xF0) | 0x08
for n in [high, low]:
try:
self.i2c.writeto(0x27, bytes([n, n | 0x04, n & ~0x04]))
except:
pass
def print(self, line1, line2=""):
# دالة مسح الشاشة وطباعة السطرين الأول والثاني
self.send(0x01, 0) # مسح الشاشة
time.sleep_ms(2) #انتظار قصير جداً (2 ميلي ثانية) بعد إرسال أمر مسح الشاشة
self.send(0x80, 0) # الانتقال للسطر الأول
for c in line1:
self.send(ord(c), 1)
if line2:
self.send(0xC0, 0) # الانتقال للسطر الثاني
for c in line2:
self.send(ord(c), 1)
# إنشاء كائن الشاشة الفعلي
lcd = SimpleLCD(i2c)
# متغيرات النظام وحالة البدء (State Machine Setup)
state = "MONITORING" # الحالة الابتدائية للنظام (مراقبة عادية)
start_time = 0 # متغير لتسجيل وقت حدوث الاصطدام
# ضبط الإضاءة والأصوات والـ LCD على الوضع الطبيعي عند بداية التشغيل
green_led.value(1)
red_led.value(0)
buzzer.duty_u16(0)
lcd.print("Status:", "Monitoring...")
# 4. الحلقة الرئيسية
while True:
g = get_accel() # قراءة قيمة التسارع الحالية
now = time.ticks_ms() # الحصول على الوقت الحالي بالملي ثانية
# --- الحالة الأولى: المراقبة الطبيعية ---
if state == "MONITORING":
if g > 2.0: # لو حدثت صدمة قوية تتجاوز 2G
state = "CHECKING" # الانتقال لحالة التحقق
start_time = now
lcd.print("Possible Impact", "Checking...")
# --- الحالة الثانية: التحقق من السكون بعد الصدمة (لتجنب الإنذارات الخاطئة) ---
elif state == "CHECKING":
if 0.5 <= g <= 1.6: # لو الجسم في نطاق السكون
# لو استمر السكون لأكثر من 1 ثانية، إذن هو سقوط حقيقي!
if time.ticks_diff(now, start_time) > 1000:
state = "FALL"
green_led.value(0) # إطفاء الأخضر
red_led.value(1) # إضاءة الأحمر
buzzer.duty_u16(30000) # تشغيل التنبيه الصوتي
lcd.print("ALERT!", "FALL DETECTED!")
elif (
time.ticks_diff(now, start_time) > 4000
): # لو مرت 4 ثواني والجسم تحرك -> إنذار كاذب
state = "MONITORING"
lcd.print("Status:", "Monitoring...")
# --- الحالة الثالثة: إنذار السقوط وثباته حتى يتم الضغط على زر الـ Reset ---
elif state == "FALL":
if reset_btn.value() == 0: # لو تم الضغط على زرار الـ Reset
state = "MONITORING" # الرجوع للوضع الطبيعي
green_led.value(1)
red_led.value(0)
buzzer.duty_u16(0)
lcd.print("Status:", "Monitoring...")
time.sleep_ms(500) # تأخير لمنع التكرار السريع للزرار (Debounce)
time.sleep_ms(50) # معدل تحديث وقراءة للحساس (20 مرة في الثانية)
# انتظار 50 ميلي ثانية في كل دورة من الحلقات التكرارية لتخفيف الضغط على المعالج وتوفير الطاقة.