الحسّاسات والمشغّلات للمتحكّمات · الفصل 13 — البيان والعرض وواجهة الإنسان والآلةחיווי, תצוגה וממשק אדם-מכונה · 33.102⌂📚🏠
الفصل 13

البيان والعرض وواجهة الإنسان والآلة

חיווי, תצוגה וממשק אדם-מכונה
الصفّان الحادي عشر والثاني عشر · 5 ساعات تعليمية

المتحكّم يعرف درجة الحرارة وحالة كل مفتاح، لكن الإنسان الواقف أمام الآلة لا يرى شيئا من ذلك. في هذا الفصل نُخرج المعلومات إلى الإنسان: مصابيح بيان بألوان متّفق عليها، طنّان يُنذر بالخطر، عارض أرقام سباعي المقاطع، شاشة نصوص وشاشة رسوم صغيرة. ثم نبني واجهة صغيرة بثلاثة أزرار نغيّر بها القيمة المطلوبة ونحفظها في ذاكرة لا تُمحى، ونختم بمبادئ تصميم واجهة الإنسان والآلة في الصناعة.

في نهاية هذا الفصل سنعرف أن:
  • لكل لون في مصباح البيان معنى ثابت حسب المواصفة IEC 60204-1، وأن الوميض يطلب الانتباه.
  • نشغّل طنّانا فعّالا أو سلبيا، وننتج نمط إنذار بـ millis() دون أن يتوقّف البرنامج.
  • الإنذار يومض حتى يُقرّ به الإنسان، ثم يثبت حتى تزول الحالة.
  • نحسب رموز الأرقام في العارض سباعي المقاطع، ونفهم مبدأ المسح المتعدّد.
  • نكتب على شاشة 16×2 وعلى شاشة OLED عبر ناقل I2C، ونرسم ما سيظهر عليهما مسبقا.
  • نبني قائمة بثلاثة أزرار بحسب مخطّط الحالات، ونحفظ القيمة المطلوبة في EEPROM.
  • نميّز بين واجهة الإنسان والآلة وواجهة آلة-آلة، ونعرف لوح اللمس الصناعي ونظام SCADA.

חיווי, התרעה ונוריות חיווי – البيان والإنذار ومصابيح البيان (Indication, Alarm, Indicator Lamps)

البيان (חיווי): هو إعلام الإنسان بحالة الآلة أو العملية: بضوء، بصوت، أو بنصّ وأرقام على شاشة. البيان لا يغيّر شيئا في العملية.
الإنذار (התרעה): هو بيان خاصّ بحالة غير طبيعية أو خطرة، يطلب انتباه الإنسان أو عملا منه. يكون عادة ضوئيا وصوتيا معا، ويبقى حتى يُقرّ به الإنسان.

أبسط وسيلة بيان هي مصباح البيان (נורית חיווי): في لوحة الكهرباء مصباح بجهد 24 V ، وفي نظام المتحكّم ديود باعث للضوء LED مع مقاومة على طرف رقمي. حتى يفهم كل إنسان اللون بنفس المعنى في كل آلة، تحدّد المواصفة IEC 60204-1 (السلامة الكهربائية للآلات) معنى كل لون:

اللونالمعنىحالة الآلة وما يفعله العامل على الآلة
أحمرطارئ חירוםحالة خطرة: عمل فوري (فصل التغذية، الابتعاد عن الآلة)
أصفرغير طبيعي חריגحالة غير طبيعية أو حرجة وشيكة: مراقبة و/أو تدخّل
أزرقإلزامي חובהحالة تتطلّب عملا من العامل: عمل إلزامي
أخضرطبيعي תקיןحالة طبيعية: لا يُطلب شيء
أبيضمحايد ניטרליحالات أخرى، أو عند الشكّ في اللون: مراقبة

العامل على الآلة (מפעיל המכונה) هو الإنسان الذي يشغّلها؛ لا نخلط بينه وبين المشغّل (المحرّك أو الصمّام). المواصفة تحدّد ألوان الأزرار أيضا: زرّ إيقاف الطوارئ أحمر على خلفية صفراء، زرّ التشغيل لا يكون أحمر، وزرّ الإيقاف لا يكون أخضر.

المصباح الثابت يصف حالة. أمّا الوميض (הבהוב) فيُستعمل حسب IEC 60204-1 لجذب الانتباه ولطلب عمل فوري، وللدلالة على فرق بين الأمر والحالة الفعلية (أمرنا الصمّام أن ينفتح ولم ينفتح بعد)، والمعلومة ذات الأولوية الأعلى تومض أسرع. نتّفق في هذا الفصل على القاعدة الشائعة في الصناعة: أحمر وامض مع طنّان = إنذار جديد، أحمر ثابت = أقرّ به الإنسان والحالة ما زالت قائمة، منطفئ = زالت الحالة.

الإقرار بالإنذار (אישור התרעה): هو ضغط الإنسان على زرّ ACK ليقول للنظام: «رأيت الإنذار». الإقرار يُسكت الطنّان ويثبّت المصباح، لكنه لا يلغي الإنذار ما دامت الحالة الخطرة قائمة.
مقاومة مصباح البيان · נגד טורי לנורית
R = VCC − VFIF
الرمزالمعنىوحدة القياس
Rالمقاومة على التوالي مع LED[Ω]
VCCجهد الطرف في الحالة HIGH (5 V في Uno)[V]
VF , IFالجهد الأمامي والتيار الأمامي للـ LED[V] , [A]
مثال 13.1
نحسب المقاومة لمصابيح بيان قطرها 5 mm ، لتيار 10 mA (أقل بكثير من 20 mA المسموحة لطرف Uno). الجهد الأمامي: أحمر 2.0 V ، أصفر 2.1 V ، أخضر 2.2 V (قيمة نموذجية حسب ورقة البيانات).
الحل:
R = 5 − 2.20.01 = 280 Ω

للأحمر 300 Ω وللأصفر 290 Ω. نختار لها كلّها القيمة القياسية 330 Ω ، فيكون التيار: أحمر 9.09 mA، أصفر 8.79 mA، أخضر 8.48 mA.

R = 330 Ω
سؤال 13.1: مصباح أخضر مع مقاومة 220 Ω على طرف 5 V. ما هو تياره؟ هل هو مسموح لطرف Uno؟

זמזם ודפוס התרעה – الطنّان ونمط الإنذار (Buzzer and Alarm Pattern)

الطنّان (זמזם): هو عنصر يحوّل الإشارة الكهربائية إلى صوت، بقرص كهرضغطي أو بملفّ وغشاء. يسمى أيضا «البازر». الطنّان الفعّال فيه مهتزّ داخلي: يكفيه جهد مستمرّ بالدالة digitalWrite() ، ويصدر تردّدا ثابتا. الطنّان السلبي بلا مهتزّ: نعطيه موجة مربّعة بالتردّد الذي نختاره، بالدالة tone().

مثلا الطنّان الفعّال TMB12A05: 5 V ، حتى 30 mA ، نحو 2300 Hz (قيمة نموذجية حسب ورقة البيانات). tone() تُخرج موجة مربّعة نسبة تشغيلها 50 % وزمن دورتها Tp = 1f حتى نستدعي noTone(). حسب توثيق Arduino: صوت واحد فقط في كل وقت، لا أقل من 31 Hz ، وفي Uno تعطّل تعديل عرض النبضة في الطرفين 3 و 11. 30 mA أكثر من الموصى بها للطرف، فنشغّل الطنّان عبر ترانزستور كمفتاح مع مقاومة قاعدة 1 kΩ (الفصل 10). وفي الطنّان المغناطيسي ملفّ، فنضع معه ديود الحماية.

Arduino Uno5VA0GNDD25V D9D5D6D7GND LM35+VSVOUTGND10 mV/°CACKINPUT_PULLUPRYG330 Ω+1 kΩ2N2222BZ
نظام إنذار: LM35 على A0 ، زرّ الإقرار على D2 ، المصابيح على D5 و D6 و D7 ، والطنّان عبر ترانزستور على D9

نمط إنذار لا يوقف البرنامج. لو كتبنا الوميض بـ delay(250) لتوقّف البرنامج في كل مرّة، ولما رأى ضغطة الإقرار في وقتها. بدل ذلك نحسب حالة الوميض من الساعة: الباقي millis() % 500 يتكرّر كل 500 ms ، ويكون أقل من 250 في نصف الوقت. الحلقة تدور آلاف المرّات في الثانية وترى كل زرّ فورا.

إنذار بثلاثة ألوان، طنّان وامض وإقرار — بلا delay Arduino Uno · alarm_ack.ino
const int SENSOR = A0;                    // LM35: 10 mV per degree C
const int GREEN_LED = 5, YELLOW_LED = 6, RED_LED = 7;
const int BUZZER = 9, ACK_BTN = 2;        // buzzer via NPN, button to GND
const float T_WARN = 45.0, T_ALARM = 50.0;

bool alarmOn = false, acked = false, buzzing = false;
float t = 0;
unsigned long tRead = 0;

void setup() {
  Serial.begin(9600);
  for (int p = 5; p <= 7; p++) pinMode(p, OUTPUT);   // three lamps
  pinMode(ACK_BTN, INPUT_PULLUP);
}

void loop() {
  unsigned long now = millis();
  if (now - tRead >= 1000) {              // one reading per second
    tRead = now;
    t = analogRead(SENSOR) * 500.0 / 1023.0;
    if (t >= T_ALARM && !alarmOn) { alarmOn = true; acked = false; }
    if (t < T_ALARM && acked) alarmOn = false;
    Serial.print("T = "); Serial.print(t, 1);
    if (!alarmOn) Serial.println(t < T_WARN ? "  GREEN" : "  YELLOW");
    else Serial.println(acked ? "  RED steady" : "  RED flashing + beep");
  }
  if (alarmOn && digitalRead(ACK_BTN) == LOW) acked = true;
  bool blink = (now % 500) < 250;         // 2 Hz, 50 %
  digitalWrite(GREEN_LED, !alarmOn && t < T_WARN);
  digitalWrite(YELLOW_LED, !alarmOn && t >= T_WARN);
  digitalWrite(RED_LED, alarmOn && (acked || blink));
  bool beep = alarmOn && !acked && blink;
  if (beep != buzzing) {                  // call tone() only on a change
    buzzing = beep;
    if (beep) tone(BUZZER, 2000); else noTone(BUZZER);
  }
}

نسخّن الحسّاس ثم نتركه يبرد. الحرارة الحقيقية في كل ثانية (°C): 41، 44، 46، 48، 51، 53، 54، 52، 49، 47، 44، ونضغط زرّ الإقرار عند t = 7.4 s. القراءة تمرّ بالمحوّل التماثلي-الرقمي، فتختلف قليلا عن الحقيقية:

Serial Monitor · قراءة نموذجية
T = 40.6  GREEN
T = 44.0  GREEN
T = 45.9  YELLOW
T = 47.9  YELLOW
T = 50.8  RED flashing + beep
T = 52.8  RED flashing + beep
T = 53.8  RED flashing + beep
T = 51.8  RED steady
T = 48.9  YELLOW
T = 46.9  YELLOW
T = 44.0  GREEN
4550T [°C]أخضرأصفرأحمرالطنّانACK024681012t [s]
مخطّط زمني من نفس النموذج: الحرارة المقروءة، المصابيح الثلاثة، الطنّان وزرّ الإقرار
سؤال 13.2: أ. ما أصغر قيمة خام N تُطلق الإنذار؟
ب. يرتفع T إلى 52 °C ثم يهبط إلى 47 °C ولم يضغط أحد الزرّ. ماذا يرى العامل؟
خطأ شائع: استدعاء tone() في كل دورة يعيد ضبط المؤقّت آلاف المرّات في الثانية، فيخرج صوت مشوّه. نستدعيها فقط عندما تتغيّر الحالة (المتغيّر buzzing).
تحذير: الطنّان التعليمي لا يحلّ محلّ نظام إنذار حريق أو غاز معتمد. وصفّارة الشبكة 230 V لا توصل بالمتحكّم إلا عبر وحدة مرحّل معتمدة ومغلقة، بإشراف المعلّم.

תצוגת שבעה מקטעים – العارض سباعي المقاطع (Seven-Segment Display)

العارض سباعي المقاطع (תצוגת שבעה מקטעים): هو سبعة مصابيح LED طويلة (مقاطع) على شكل الرقم 8، أسماؤها a حتى g ، ومعها غالبا نقطة DP. يسمى أيضا «شاشة العرض ذات السبع قطع».
agdfbecDP
أسماء المقاطع a – g والنقطة DP
abcdefgCOM
مهبط مشترك: COM إلى الأرضي، والمقطع يضيء بـ HIGH
abcdefgCOM
مصعد مشترك: COM إلى 5 V ، والمقطع يضيء بـ LOW

في العارض ذي المهبط المشترك (קתודה משותפת, CC) نوصل COM بالأرضي ويضيء المقطع بـ HIGH؛ وفي العارض ذي المصعد المشترك (אנודה משותפת, CA) نوصل COM بـ 5 V ويضيء المقطع بـ LOW (مثلا 5161AS مهبط مشترك و 5161BS مصعد مشترك). نكتب لكل رقم بايتا: الخانة 0 للمقطع a … الخانة 6 للمقطع g ، و 1 = مضاء في المهبط المشترك. في المصعد المشترك نقلب الخانات السبع:

الرقمabcdefgالرمز (CC)الرمز (CA)
011111100x3F0x40
101100000x060x79
211011010x5B0x24
311110010x4F0x30
401100110x660x19
510110110x6D0x12
610111110x7D0x02
711100000x070x78
811111110x7F0x00
911110110x6F0x10
مثال 13.2
نحسب رمز الرقم 4 في المهبط المشترك وفي المصعد المشترك.
الحل:

الرقم 4 يضيء b ، c ، f ، g ، أي الخانات 1 ، 2 ، 5 ، 6:

CC = 21 + 22 + 25 + 26 = 102 = 0x66
CA = 127 − 102 = 25 = 0x19
CC = 0x66 ; CA = 0x19
Arduino UnoD2D3D4D5D6D7D8GND5161ASabcdefgCOM7 × 330 Ω(CC)
عارض بمهبط مشترك: المقاطع a – g إلى D2 – D8 ، كل مقطع مع 330 Ω

لكل مقطع مقاومة خاصّة: مقاومة واحدة في COM تجعل الرقم 1 ساطعا والرقم 8 خافتا. مع VF ≈ 2 V (قيمة نموذجية حسب ورقة البيانات لـ LED أحمر) تيار المقطع I = 5 − 2330 ≈ 9.1 mA. في البرنامج bitRead(x, i) تقرأ الخانة i ، والتعبير on != COMMON_ANODE يقلب المنطق: للمصعد المشترك نغيّر سطرا واحدا، COMMON_ANODE = true.

عدّ من 0 إلى 9 والرموز في مصفوفة Arduino Uno · seven_segment_digit.ino
const int SEG_PIN[7] = {2, 3, 4, 5, 6, 7, 8};   // segments a..g
const bool COMMON_ANODE = false;                // 5161AS: common cathode
// bit 0 = a ... bit 6 = g ; 1 = segment lit
const byte DIGIT[10] = {0x3F, 0x06, 0x5B, 0x4F, 0x66,
                        0x6D, 0x7D, 0x07, 0x7F, 0x6F};

void showDigit(int d) {
  for (int i = 0; i < 7; i++) {
    bool on = bitRead(DIGIT[d], i);
    digitalWrite(SEG_PIN[i], on != COMMON_ANODE);  // anode: lit = LOW
  }
}

void setup() {
  for (int i = 0; i < 7; i++) pinMode(SEG_PIN[i], OUTPUT);
}

void loop() {
  for (int d = 0; d < 10; d++) {
    showDigit(d);
    delay(1000);
  }
}
المسح المتعدّد (ריבוב תצוגה): هو تشغيل عدّة أرقام بنفس أطراف المقاطع: نضيء رقما واحدا في كل لحظة وننتقل بسرعة إلى التالي، فترى العين كل الأرقام مضاءة معا. يسمى أيضا «تعدّد الإرسال الزمني».

لأربعة أرقام نحتاج 7 + 4 = 11 طرفا بدل 28. طرف COM لكل رقم يمرّ عبر ترانزستور: تيار الرقم 8 7·9.09 ≈ 63.6 mA ، أكثر من 40 mA ، الحدّ الأقصى المطلق لطرف Uno.

مثلا في عارض بأربعة أرقام، كل رقم مضاء 4 ms ، يعود كل رقم بعد 4·4 = 16 ms ، أي نحو 62.5 مرّة في الثانية: أسرع مما تلاحظه العين، فلا نرى رجفة. لكن كل رقم مضاء ربع الوقت فقط، فيبدو أخفت من الإضاءة الثابتة. وفي البرنامج الحقيقي ننتقل للرقم التالي بـ millis() لا بـ delay() ، أو نستعمل رقاقة مسح جاهزة مثل MAX7219.

צג גביש נוזלי 16×2 באפיק I2C – شاشة الكريستال السائل 16×2 عبر I2C (Character LCD over I2C)

شاشة الكريستال السائل (צג גביש נוזלי): هي شاشة نصّ: في الشاشة 16×2 سطران في كل منهما 16 خانة، وكل خانة نقاط 5×8. يتحكّم بها المتحكّم الداخلي HD44780، ولها إضاءة خلفية. تسمى أيضا LCD.

خلف الشاشة لوحة فيها الرقاقة PCF8574، فتكفي أربعة أسلاك: التغذية والأرضي و ناقل I2C — سلكان فقط، SDA للبيانات و SCL للساعة، تشترك فيهما عدّة وحدات لكلّ منها عنوان — وفي Uno SDA هو A4 و SCL هو A5. عنوان (כתובת) اللوحة 0x27 مع PCF8574 و 0x3F مع PCF8574A (قيمة نموذجية حسب ورقة البيانات). الأوامر: init() ، backlight() للإضاءة، setCursor(c, r) للعمود c والسطر r (العدّ من 0)، و print().

Arduino UnoGND5VA4A55V A0GND LCD I2CGNDVCCSDASCLPCF8574 · 0x27 / 0x3FLM35+VSVOUTGND
الشاشة على ناقل I2C (A4 ، A5)، والحسّاس LM35 على A0
الحرارة والقيمة المطلوبة على شاشة 16×2 Arduino Uno · lcd_temperature.ino
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

const int SENSOR = A0;                // LM35: 10 mV per degree C
const float SET_POINT = 30.0;         // [C]
LiquidCrystal_I2C lcd(0x27, 16, 2);   // 0x3F on some modules

void setup() {
  lcd.init();
  lcd.backlight();
}

void loop() {
  float t = analogRead(SENSOR) * 500.0 / 1023.0;
  bool heat = t < SET_POINT;          // heater demand
  lcd.setCursor(0, 0);                // column 0, row 0
  lcd.print("T  = "); lcd.print(t, 1);
  lcd.print((char)223); lcd.print("C    ");   // 223 = degree sign
  lcd.setCursor(0, 1);
  lcd.print("SP = "); lcd.print(SET_POINT, 1);
  lcd.print((char)223); lcd.print(heat ? "C  ON " : "C  OFF");
  delay(500);
}
T=24.4°CSP=30.0°CON
N = 50 : أبرد من المطلوب، التسخين مطلوب
T=31.3°CSP=30.0°COFF
N = 64 : أسخن من المطلوب، التسخين متوقّف

الرسمان محسوبان خانة خانة من نفس السلاسل: مثلا N = 50 يعطي T = 50·5001023 = 24.438 °C ، و print(t, 1) يقرّب إلى 24.4. الرمز 223 في جدول حروف HD44780 هو «°». ونكتب مسافات في نهاية السطر: الشاشة لا تمحو ما كُتب سابقا، فلو قصر النصّ (من 100.0 إلى 99.5) لبقي حرف قديم. ولا نستدعي lcd.clear() في كل دورة، فهو يمحو كل الشاشة ونرى رجفة.

سؤال 13.3: الشاشة مضاءة لكنها لا تُظهر أيّ حرف، والتوصيل صحيح. ما هما السببان الأكثر شيوعا؟

צג OLED גרפי 128×64 – شاشة OLED الرسومية 128×64 (Graphic OLED Display)

شاشة OLED (צג OLED): هي شاشة رسومية كل نقطة (بكسل) فيها ديود عضوي يضيء بنفسه، فلا تحتاج إلى إضاءة خلفية. الشاشة الشائعة 128×64 بكسل يتحكّم بها SSD1306، وعنوانها على ناقل I2C 0x3C (أو 0x3D).

النقطة (0, 0) في الزاوية العليا اليسرى، x إلى اليمين حتى 127 و y إلى الأسفل حتى 63. المكتبة Adafruit_SSD1306 (مع Adafruit_GFX) ترسم أوّلا في ذاكرة المتحكّم، ثم ترسل الصورة كلّها بـ display(). كل بكسل خانة، فالصورة 128·648 = 1024 بايت: نصف ذاكرة Uno (2048 بايت). المكتبة تحجزها في begin() ، فلا تظهر في تقرير الترجمة، وإذا لم يبق مكان تعيد begin() القيمة false.

Arduino UnoGND5VA4A55V A0GND OLED 0x3CGNDVCCSDASCLSSD1306 · 128×6410 kΩ
شاشة OLED على أطراف I2C، ومقاومة متغيّرة على A0 بدل حسّاس مستوى
مستوى الخزّان رقما وعمودا أفقيا Arduino Uno · oled_bar_graph.ino
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

const int SENSOR = A0;                      // potentiometer as a level sensor
Adafruit_SSD1306 oled(128, 64, &Wire, -1);  // -1: no reset pin

void setup() {
  if (!oled.begin(SSD1306_SWITCHCAPVCC, 0x3C))
    while (true) {}                         // no screen or no RAM: stop
}

void loop() {
  int pct = map(analogRead(SENSOR), 0, 1023, 0, 100);
  int w = map(pct, 0, 100, 0, 124);           // bar width [pixels]
  oled.clearDisplay();
  oled.setTextColor(SSD1306_WHITE);
  oled.setTextSize(1); oled.setCursor(0, 0);
  oled.print("TANK LEVEL");
  oled.setTextSize(2); oled.setCursor(0, 16);
  oled.print(pct); oled.print(" %");
  oled.drawRect(0, 40, 128, 20, SSD1306_WHITE);  // frame
  oled.fillRect(2, 42, w, 16, SSD1306_WHITE);    // bar
  oled.display();                           // send the buffer
  delay(200);
}
TANK LEVEL68 %
الشاشة عندما N = 700

الرسم محسوب بكسلا بكسلا من نفس الأوامر. حرف الخطّ الأساسي 5×7 بكسل في خانة 6×8 ، و setTextSize(2) يضاعف الأبعاد. في بعض الوحدات الصفوف الستة عشر العليا صفراء والباقي أزرق، ولذلك العنوان في y < 16.

مثال 13.3
نحسب النسبة المئوية وعرض العمود عندما N = 700.
الحل:
pct = ⌊700·1001023⌋ = 68
w = ⌊68·124100⌋ = 84

الدالة map() تحسب بأعداد صحيحة وتحذف الكسر. العمود يبدأ في x = 2 وينتهي في x = 85 ، داخل الإطار.

pct = 68 ; w = 84
سؤال 13.4: الشاشة LCD (0x27) وشاشة OLED (0x3C) موصولتان معا بالطرفين A4 و A5. هل يعمل هذا؟
خطأ شائع: على كثير من الوحدات مكتوب 0x78: هذا العنوان مضروبا في 2 مع خانة القراءة/الكتابة (0x3C·2 = 0x78). في البرنامج نكتب العنوان ذا السبع خانات 0x3C.

ממשק קטן: שלושה לחצנים וערך רצוי ב-EEPROM – واجهة صغيرة: ثلاثة أزرار وقيمة مطلوبة في EEPROM (Mini HMI with EEPROM)

في المنظّم الحراري يريد العامل أن يغيّر القيمة المطلوبة (ערך רצוי) بلا حاسوب، وأن تبقى بعد انقطاع الكهرباء. نبني واجهة بثلاثة أزرار UP ، DOWN ، OK، ونصف سلوكها أوّلا بـ مخطّط الحالات (תרשים מצבים)، ثم نكتب البرنامج منه:

RUNعرض القيمEDITتعديل القيمة المطلوبةEEPROM.getتشغيلOKspEdit = spOKEEPROM.putمهلة 10 ثوانٍ بلا مفتاح: بلا حفظUP / DOWN±0.5 °CspEdit
مخطّط حالات القائمة: حالتان، والانتقال بالأزرار أو بانتهاء المهلة
ذاكرة EEPROM (זיכרון EEPROM): هي ذاكرة داخل المتحكّم تبقى فيها البيانات بعد فصل التغذية. في ATmega328P (Uno) حجمها 1024 بايت، وكل خلية تتحمّل نحو 100000 مرّة كتابة (قيمة نموذجية حسب ورقة البيانات). في الخلية الجديدة 0xFF.

نخزّن القيمة بأعشار الدرجة كعدد صحيح int (بايتان): 25.5 °C تُخزَّن 255. EEPROM.get() تقرأ، و EEPROM.put() تكتب فقط البايتات التي تغيّرت. في الذاكرة الجديدة يُقرأ −1 ، فنفحص المدى ونضع قيمة افتراضية.

Arduino UnoGND5VA4A5D2D3D4GND A05V LCD I2CGNDVCCSDASCL0x27 · 16×2UPDOWNOKLM35GNDVOUT+VSINPUT_PULLUP
ثلاثة أزرار إلى الأرضي على D2 ، D3 ، D4 ، الشاشة على I2C والحسّاس على A0
قائمة بثلاثة أزرار، مهلة، وحفظ القيمة المطلوبة في EEPROM Arduino Uno · mini_hmi_eeprom.ino
#include <Wire.h>
#include <EEPROM.h>
#include <LiquidCrystal_I2C.h>

const int BTN[3] = {2, 3, 4};             // UP, DOWN, OK (to GND)
const int SENSOR = A0, ADDR = 0;          // LM35 ; EEPROM address
const int SP_MIN = 100, SP_MAX = 600, STEP = 5;   // tenths of C
const unsigned long TIMEOUT = 10000;      // ms without a key
LiquidCrystal_I2C lcd(0x27, 16, 2);
int sp, spEdit;                           // set-point x 10: 255 = 25.5 C
bool editing = false;
unsigned long tKey = 0;
int last[3] = {HIGH, HIGH, HIGH};

bool pressed(int i) {                     // HIGH -> LOW edge
  int now = digitalRead(BTN[i]);
  bool edge = (last[i] == HIGH && now == LOW);
  last[i] = now;
  return edge;
}

void setup() {
  Serial.begin(9600);
  for (int i = 0; i < 3; i++) pinMode(BTN[i], INPUT_PULLUP);
  lcd.init(); lcd.backlight();
  EEPROM.get(ADDR, sp);
  if (sp < SP_MIN || sp > SP_MAX) sp = 250;   // new EEPROM reads -1
  Serial.print("SP from EEPROM = "); Serial.println(sp / 10.0, 1);
}

void loop() {
  bool up = pressed(0), down = pressed(1), ok = pressed(2);
  if (up || down || ok) tKey = millis();
  if (!editing) {
    if (ok) { editing = true; spEdit = sp; Serial.println("EDIT"); }
  } else {
    if (up)   spEdit = min(spEdit + STEP, SP_MAX);
    if (down) spEdit = max(spEdit - STEP, SP_MIN);
    if (up || down) {
      Serial.print("SP* = "); Serial.println(spEdit / 10.0, 1);
    }
    if (ok) {                             // save only on OK
      sp = spEdit; editing = false; EEPROM.put(ADDR, sp);
      Serial.print("SAVED SP = "); Serial.println(sp / 10.0, 1);
    } else if (millis() - tKey > TIMEOUT) {
      editing = false; Serial.println("TIMEOUT: no change");
    }
  }
  float t = analogRead(SENSOR) * 500.0 / 1023.0;
  lcd.setCursor(0, 0); lcd.print("T  = "); lcd.print(t, 1); lcd.print(" C  ");
  lcd.setCursor(0, 1); lcd.print(editing ? "SP*= " : "SP = ");
  lcd.print((editing ? spEdit : sp) / 10.0, 1); lcd.print("  ");
  delay(20);                              // 20 ms sampling: no bounce
}

نشغّل المتحكّم لأوّل مرّة (EEPROM جديدة) ونضغط بالترتيب: OK ← UP ← UP ← UP ← DOWN ← OK ← OK ← UP ← انتظار 11 s:

Serial Monitor · قراءة نموذجية
SP from EEPROM = 25.0
EDIT
SP* = 25.5
SP* = 26.0
SP* = 26.5
SP* = 26.0
SAVED SP = 26.0
EDIT
SP* = 26.5
TIMEOUT: no change
T=23.0CSP*=26.5
الشاشة في EDIT بعد ثلاث ضغطات UP (N = 47)

بعد فصل التغذية وإعادتها يطبع البرنامج «SP from EEPROM = 26.0»: القيمة محفوظة. الحلقة تتوقّف 20 ms في كل دورة؛ هذا أطول من ارتداد التلامس فيكفي لإزالة الارتداد، وما زال سريعا جدا للإنسان.

مثال 13.4
برنامج خاطئ يكتب القيمة في EEPROM كل ثانية. بعد كم ساعة قد تتلف الخلية؟ وكم سنة تعيش إذا كتبنا فقط عند OK ، 20 مرّة في اليوم؟
الحل:
t = 100000·1 s = 27.8 h

ومع 20 كتابة في اليوم: t = 10000020 = 5000 يوما، أي نحو 13.7 سنة.

لذلك نكتب في EEPROM فقط عند تأكيد الإنسان، وليس في كل دورة من loop().

t ≈ 27.8 h
خطأ شائع: الأزرار إلى الأرضي مع INPUT_PULLUP ، فالضغطة LOW. نكشف الحافة (من HIGH إلى LOW) لا المستوى: لو فحصنا المستوى لزادت القيمة 50 مرّة في الثانية ما دام الإصبع على الزرّ.
▶تجربة حية — واجهة الإنسان والآلةنجرّب واجهة تشغيل: مصابيح بيان، إنذار وإقرار، وتغيير القيمة المطلوبة.
⚠
المختبر غير متاح الآن. المادة في هذا الفصل كاملة من دونه — نواصل القراءة، ونعود إلى التجربة لاحقا أو نفتحها مباشرة من الرابط الذي تحت الإطار.
https://fahed-lab.com/hashmal/sensors#hmi
نفتح المختبر في صفحة مستقلة ↗ fahed-lab.com/hashmal/sensors#hmi

ממשק אדם-מכונה וממשק מכונה-מכונה – واجهة الإنسان والآلة وواجهة آلة-آلة (HMI and M2M)

الواجهة (ממשק) هي الحدّ الذي تنتقل عبره المعلومات بين طرفين في نظام التحكّم. نصفها بأربعة أسئلة: من الطرفان؟ أيّ معلومات تنتقل وفي أيّ اتّجاه؟ بأيّ وسيلة (أسلاك، جهد، بروتوكول)؟ وبأيّ سرعة وموثوقية؟

واجهة الإنسان والآلة (ממשק אדם-מכונה (HMI)): هي الواجهة بين الإنسان والآلة: الأزرار والمصابيح والشاشات والطنّانات. غايتها أن يفهم الإنسان حالة العملية بسرعة وبلا خطأ، وأن يعطي أوامره بأمان.
واجهة آلة-آلة (ממשק מכונה-מכונה (M2M)): هي واجهة يتبادل فيها جهازان المعلومات تلقائيا بلا إنسان: متحكّم يقرأ حسّاسا بناقل I2C، حلقة التيار 4–20 mA ، أو متحكّمان منطقيان مبرمجان (בקר מתוכנת, PLC) يتبادلان القيم عبر شبكة صناعية.
▶تجربة حية — لوحات التحكّمنتعرّف على لوحات التحكّم في الصناعة: الأزرار ومصابيح البيان وألوانها.
⚠
المختبر غير متاح الآن. المادة في هذا الفصل كاملة من دونه — نواصل القراءة، ونعود إلى التجربة لاحقا أو نفتحها مباشرة من الرابط الذي تحت الإطار.
https://fahed-lab.com/hashmal/sensors#panels
نفتح المختبر في صفحة مستقلة ↗ fahed-lab.com/hashmal/sensors#panels
واجهة الإنسان والآلةواجهة آلة-آلة
شكل المعلومةألوان، رموز، نصوص، رسوم، أصواتأعداد وإشارات بحسب بروتوكول
السرعةسرعة الإنسان: أجزاء من الثانية تكفيمن ميكروثوانٍ إلى ثوانٍ
أمثلةالمصابيح، العارض، LCD ، OLED ، الأزرارI2C ، السلك الواحد، حلقة التيار
الآلة 1حسّاسات ومشغّلاتالآلة 2حسّاسات ومشغّلاتمتحكّم / PLC 1متحكّم / PLC 2واجهة آلة-آلةبيانات بلا إنسانلوح لمس HMIبجانب الآلةإنسان-آلةSCADAغرفة التحكّم: مراقبة، سجلّ، إنذاراتشبكة صناعية
طبقات المصنع: الآلات، المتحكّمات، لوح اللمس بجانب الآلة، و SCADA في غرفة التحكّم

لوح اللمس الصناعي (פנל HMI / מסך מגע תעשייתי) شاشة ملوّنة في باب لوحة الكهرباء، موصولة بالـ PLC. عليها رسم مبسّط للآلة تتغيّر ألوانه مع الحالة، أزرار، قيم مطلوبة، وصفحة إنذارات. نظام SCADA (מערכת SCADA – פיקוח ואיסוף נתונים) برنامج في حاسوب بغرفة التحكّم، يجمع البيانات من متحكّمات كثيرة في المصنع أو في محطّات بعيدة، يعرضها، يحفظ سجلّا لها على مرّ الزمن، يدير الإنذارات ويرسل أوامر. التحكّم السريع يبقى في الـ PLC: إذا انقطعت الشبكة تستمرّ الآلة في العمل الآمن.

مبادئ تصميم الواجهة – עקרונות תכנון ממשק:

  1. الوضوح وردّ الفعل: كل شاشة تجيب على سؤال واحد، والقيم مع وحداتها (°C ، bar)، وكل ضغطة تُرى أو تُسمع.
  2. ألوان متّسقة: الأحمر للخطر فقط والأخضر للطبيعي فقط، في كل الشاشات والمصابيح. ولا نعتمد على اللون وحده: نضيف نصّا أو رمزا، فبعض الناس لا يميّزون الأحمر من الأخضر.
  3. تأكيد الأعمال الخطرة: أمر قد يسبّب ضررا («تفريغ الخزّان») يحتاج خطوتين: الأمر ثم «هل أنت متأكّد؟». وإيقاف الطوارئ ليس زرّا على الشاشة أبدا، بل زرّ مستقلّ في دائرة السلامة.
  4. الإقرار بالإنذارات: إنذار جديد يومض ويُسمع حتى الإقرار، ويقول ماذا حدث، أين، وماذا نفعل. وعدد الإنذارات قليل وبأولويات: مئة إنذار في دقيقة لا يقرؤها أحد.
سؤال 13.5: في لوح لمس رُسم زرّ «تشغيل المضخّة» بالأحمر، ومصباح «المضخّة تعمل بشكل طبيعي» بالأحمر أيضا. ما الخطأان؟

ملخّص قوانين الفصل:

القانونالصيغة
مقاومة مصباح البيانR = VCC − VFIF
زمن دورة صوت الطنّانTp = 1f
القيمة الخام من LM35 إلى درجاتT = N·5001023
رمز الرقم في المصعد المشتركCA = 127 − CC
ذاكرة صورة OLEDbuffer = W·H8

قاموس المصطلحات – מילון מונחים:

بالعربيةبالعبرية (كما تظهر في الامتحان)English
البيانחיוויIndication
الإنذارהתרעהAlarm
مصباح البيانנורית חיוויIndicator lamp
الوميضהבהובFlashing
الإقرار بالإنذارאישור התרעהAlarm acknowledge
العامل على الآلةמפעיל המכונהMachine operator
الطنّان (البازر) فعّال · سلبيזמזם אקטיבי · פסיביActive · passive buzzer
العارض سباعي المقاطعתצוגת שבעה מקטעיםSeven-segment display
المهبط المشترك · المصعد المشتركקתודה משותפת · אנודה משותפתCommon cathode · common anode
المسح المتعدّد (تعدّد الإرسال الزمني)ריבוב תצוגהDisplay multiplexing
شاشة الكريستال السائل (LCD)צג גביש נוזליLiquid crystal display
ناقل I2C · العنوانאפיק I2C · כתובתI2C bus · address
شاشة OLEDצג OLEDOLED display
القيمة المطلوبةערך רצויSet-point
مخطّط الحالاتתרשים מצביםState diagram
ذاكرة EEPROMזיכרון EEPROMEEPROM
الواجهةממשקInterface
واجهة الإنسان والآلةממשק אדם-מכונהHuman-machine interface (HMI)
واجهة آلة-آلةממשק מכונה-מכונהMachine-to-machine interface (M2M)
لوح اللمس الصناعيפנל HMI / מסך מגע תעשייתיIndustrial touch panel
المتحكّم المنطقي المبرمجבקר מתוכנת (PLC)Programmable logic controller
نظام SCADAמערכת SCADASupervisory control and data acquisition

أسئلة للتمرين:

1.
في آلة يضيء مصباح أصفر ثابت. ما معناه حسب IEC 60204-1، وماذا يفعل العامل؟ وما معنى المصباح الأزرق؟
الإجابات: حالة غير طبيعية أو حالة حرجة وشيكة: مراقبة و/أو تدخّل. الأزرق: عمل إلزامي مطلوب من العامل
2.
مصباح أصفر VF = 2.1 V على طرف 5 V ، والتيار المطلوب 10 mA.
أ. نحسب المقاومة.
ب. نختار القيمة القياسية الأكبر التالية ونحسب التيار الفعلي.
الإجابات: أ. R = 290 Ω. ب. 330 Ω ، IF ≈ 8.79 mA
3.
طنّان سلبي يعمل بـ tone(9, 2500). نحسب زمن الدورة وزمن الحالة HIGH في كل دورة بوحدة µs.
الإجابات: Tp = 400 µs ، tHIGH = 200 µs
4.
نمط إنذار: الطنّان يعمل 150 ms ويسكت 350 ms ، ويتكرّر.
أ. كم صفيرا في الثانية؟
ب. ما نسبة التشغيل؟
ج. نكتب الشرط بـ millis().
الإجابات: أ. 2 في الثانية. ب. 30 %. ج. (millis() % 500) < 150
5.
نكتب رمز الحرف H (المقاطع b ، c ، e ، f ، g) في المهبط المشترك وفي المصعد المشترك.
الإجابات: CC = 0x76 ، CA = 0x09
6.
في عارض بمهبط مشترك كل مقطع مع 470 Ω ، و VF = 2 V.
أ. نحسب تيار المقطع.
ب. نحسب التيار في COM عند عرض الرقم 8.
ج. هل يمكن وصل COM بطرف من Uno مباشرة؟
الإجابات: أ. I ≈ 6.38 mA. ب. 44.7 mA. ج. لا: أكثر من 40 mA ، نستعمل ترانزستورا
7.
عارض بستّة أرقام بالمسح المتعدّد، وكل رقم مضاء 2.5 ms. بعد كم ميلي ثانية يعود كل رقم؟ كم مرّة في الثانية يُضاء؟ هل نرى رجفة؟
الإجابات: بعد 15 ms ، نحو 66.7 مرّة في الثانية؛ لا رجفة
8.
في البرنامج lcd_temperature قرأ المتحكّم N = 63. ماذا يُكتب في السطرين؟
الإجابات: السطر الأول «T = 30.8°C»، والسطر الثاني «SP = 30.0°C OFF»
9.
في البرنامج oled_bar_graph النسبة 45 %.
أ. ما عرض العمود بالبكسل؟
ب. كم بايتا تحتاج صورة شاشة 128×32؟
الإجابات: أ. w = 55. ب. 512 بايت
10.
برنامج يحفظ القيمة المطلوبة في EEPROM كل 30 s.
أ. بعد كم يوما قد تتلف الخلية؟
ب. نقترح تعديلا في البرنامج.
الإجابات: أ. نحو 34.7 يوما. ب. نكتب فقط عند الضغط على OK (أو عند تغيّر القيمة)
11.
نضيف إلى مخطّط حالات الواجهة حالة ثالثة ALARM: تدخلها من RUN عندما T ≥ 50 °C ، وتخرج منها إلى RUN بعد الإقرار وزوال الحالة. نرسم المخطّط الجديد ونكتب على كل سهم شرط الانتقال.
الإجابات: ثلاث حالات RUN ، EDIT ، ALARM؛ RUN→ALARM عند T ≥ 50 ، ALARM→RUN عند ACK و T < 50
12.
نصمّم صفحة في لوح لمس لخزّان ماء. نذكر ثلاثة مبادئ تصميم ونطبّق كل واحد منها على الصفحة.
الإجابات: مثلا: مستوى بالأرقام مع الوحدة؛ أخضر للطبيعي وأحمر للفيض مع نصّ؛ تأكيد قبل «تفريغ الخزّان»
الإجابات النهائية لأسئلة الفصل
13.1 IF ≈ 12.7 mA ، مسموح (أقل من 20 mA)
13.2 أ. N = 103 (لأن 50·1023500 = 102.3). ب. ما زال الأحمر يومض والطنّان يعمل حتى الإقرار
13.3 العنوان غير صحيح (نجرّب 0x3F بدل 0x27)، أو التباين غير مضبوط (المقاومة المتغيّرة في اللوحة الخلفية)
13.4 نعم: لكل جهاز عنوان مختلف على نفس الناقل
13.5 زرّ التشغيل لا يكون أحمر؛ والحالة الطبيعية خضراء (الأحمر للخطر فقط)
لوحة القوانين: كل قوانين هذا الفصل مجموعة مع رسومها وجداول رموزها في الفصل 15 — لوحة القوانين الشاملة. (נוסחאון הספר)