نظام طبي لقياس نسبة الأكسجين في الدم ومعدل ضربات القلب

مقدمة

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

arduino-blood-oxygen-heart-rate-monitor-max30100

المواد والأدوات

arduino-blood-oxygen-heart-rate-monitor-max30100

1× اردوينو اونو

arduino-blood-oxygen-heart-rate-monitor-max30100

1× سلك الاردوينو

breadboard

1× لوحة تجارب – حجم كبير

arduino-blood-oxygen-heart-rate-monitor-max30100

3× مقاومة 4.7K Ωنظام طبي

مستشعر معدل نبضات القلب والأكسجين

arduino-blood-oxygen-heart-rate-monitor-max30100 اسلاك التوصيل لوح التجارب

arduino-blood-oxygen-heart-rate-monitor-max30100

1× شاشة (OLED)نظام طبي

موديول بلوتوث من النوع HC-06

arduino-blood-oxygen-heart-rate-monitor-max30100

حزمة أسلاك توصيل (ذكر – أنثى)

arduino-blood-oxygen-heart-rate-monitor-max30100

1× هاتف بنظام اندرويد

توصيل الدائرة

للمزيد حول وحدة البلوتوث يمكنك الرجوع للدرس التالي نظام التحكم في الإضاءة عبر البلوتوثنظام طبي

الكود البرمجي

ارفع الكود البرمجي على لوحة الاردوينو باستخدام برنامج اردوينو (IDE).

#include <Wire.h>
#include "MAX30100_PulseOximeter.h"
#include "Wire.h"
#include "Adafruit_GFX.h"
#include "OakOLED.h"
#define REPORTING_PERIOD_MS 1000
OakOLED oled;
PulseOximeter pox;

uint32_t tsLastReport = 0;

const unsigned char bitmap [] PROGMEM=
{
0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x18, 0x00, 0x0f, 0xe0, 0x7f, 0x00, 0x3f, 0xf9, 0xff, 0xc0,
0x7f, 0xf9, 0xff, 0xc0, 0x7f, 0xff, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff, 0xf0,
0xff, 0xf7, 0xff, 0xf0, 0xff, 0xe7, 0xff, 0xf0, 0xff, 0xe7, 0xff, 0xf0, 0x7f, 0xdb, 0xff, 0xe0,
0x7f, 0x9b, 0xff, 0xe0, 0x00, 0x3b, 0xc0, 0x00, 0x3f, 0xf9, 0x9f, 0xc0, 0x3f, 0xfd, 0xbf, 0xc0,
0x1f, 0xfd, 0xbf, 0x80, 0x0f, 0xfd, 0x7f, 0x00, 0x07, 0xfe, 0x7e, 0x00, 0x03, 0xfe, 0xfc, 0x00,
0x01, 0xff, 0xf8, 0x00, 0x00, 0xff, 0xf0, 0x00, 0x00, 0x7f, 0xe0, 0x00, 0x00, 0x3f, 0xc0, 0x00,
0x00, 0x0f, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

void onBeatDetected()
{
Serial.println("Beat!");
oled.drawBitmap( 60, 20, bitmap, 28, 28, 1);
oled.display();
}

void setup()
{
Serial.begin(9600);

oled.begin();
oled.clearDisplay();
oled.setTextSize(1);
oled.setTextColor(1);
oled.setCursor(0, 0);

oled.println("Initializing pulse oximeter..");
oled.display();
Serial.print("Initializing pulse oximeter..");

if (!pox.begin()) {
Serial.println("FAILED");
oled.clearDisplay();
oled.setTextSize(1);
oled.setTextColor(1);
oled.setCursor(0, 0);
oled.println("FAILED");
oled.display();
for(;;);
} else {
oled.clearDisplay();
oled.setTextSize(1);
oled.setTextColor(1);
oled.setCursor(0, 0);
oled.println("SUCCESS");
oled.display();
Serial.println("SUCCESS");
}
pox.setOnBeatDetectedCallback(onBeatDetected);
}

void loop()
{
pox.update();

if (millis() - tsLastReport > REPORTING_PERIOD_MS) {
Serial.print("Heart BPM:");
Serial.print(pox.getHeartRate());
Serial.print("-----");
Serial.print("Oxygen Percent:");
Serial.print(pox.getSpO2());
Serial.println("\n");
oled.clearDisplay();
oled.setTextSize(1);
oled.setTextColor(1);
oled.setCursor(0,16);
oled.println(pox.getHeartRate());

oled.setTextSize(1);
oled.setTextColor(1);
oled.setCursor(0, 0);
oled.println("Heart BPM");

oled.setTextSize(1);
oled.setTextColor(1);
oled.setCursor(0, 30);
oled.println("Spo2");

oled.setTextSize(1);
oled.setTextColor(1);
oled.setCursor(0,45);
oled.println(pox.getSpO2());
oled.display();
tsLastReport = millis();
}
}

شرح الكود البرمجي

هنا سيتم تعريف المكتبات المستخدمة في المشروع.

عليك تحميل المكتبات التالية:

1. Arduino MAX30100 Library
2. OkaOLED Library
3. Adafruit GFX Library

وذلك بالرجوع للملف المضغوط، لمعرفة كيفية تنزيل المكتبات يمكنك الرجوع إلى الدرس التالي.

#include <Wire.h>
#include "MAX30100_PulseOximeter.h"
#include "Wire.h"
#include "Adafruit_GFX.h"
#include "OakOLED.h"

هنا سيتم تعريف المتغير الخاص بالشاشة OLED.

OakOLED oled;

وهنا سيتم تعريف المتغير الخاص بمستشعر نبضات القلب والأكسجين.

PulseOximeter pox;

في هذه الأسطر يتم تهيئة مصفوفة تضم مجموعة من المتغيرات والتي تخص الشاشة OLED.

const unsigned char bitmap [] PROGMEM=
{
0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x18, 0x00, 0x0f, 0xe0, 0x7f, 0x00, 0x3f, 0xf9, 0xff, 0xc0,
0x7f, 0xf9, 0xff, 0xc0, 0x7f, 0xff, 0xff, 0xe0, 0x7f, 0xff, 0xff, 0xe0, 0xff, 0xff, 0xff, 0xf0,
0xff, 0xf7, 0xff, 0xf0, 0xff, 0xe7, 0xff, 0xf0, 0xff, 0xe7, 0xff, 0xf0, 0x7f, 0xdb, 0xff, 0xe0,
0x7f, 0x9b, 0xff, 0xe0, 0x00, 0x3b, 0xc0, 0x00, 0x3f, 0xf9, 0x9f, 0xc0, 0x3f, 0xfd, 0xbf, 0xc0,
0x1f, 0xfd, 0xbf, 0x80, 0x0f, 0xfd, 0x7f, 0x00, 0x07, 0xfe, 0x7e, 0x00, 0x03, 0xfe, 0xfc, 0x00,
0x01, 0xff, 0xf8, 0x00, 0x00, 0xff, 0xf0, 0x00, 0x00, 0x7f, 0xe0, 0x00, 0x00, 0x3f, 0xc0, 0x00,
0x00, 0x0f, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};

في الدالة ()onBeatDetected سيتم قراءة البيانات من مستشعر نبضات القلب والأكسجين وطباعتها على الشاشة OLED.

void onBeatDetected()
{
Serial.println("Beat!");
oled.drawBitmap( 60, 20, bitmap, 28, 28, 1);
oled.display();
}

في الدالة ()setup سيتم طباعة القيم المقروءة من مستشعر نبضات القلب والأكسجين وإذا تعذر ذلك سيتم طباعة الكلمة Failed بمعنى فشلت عملية القراءة هنا لابد التأكد من توصيل المستشعر بالشكل الصحيح.

void setup()
{
Serial.begin(9600);

oled.begin();
oled.clearDisplay();
oled.setTextSize(1);
oled.setTextColor(1);
oled.setCursor(0, 0);

oled.println("Initializing pulse oximeter..");
oled.display();
Serial.print("Initializing pulse oximeter..");

if (!pox.begin()) {
Serial.println("FAILED");
oled.clearDisplay();
oled.setTextSize(1);
oled.setTextColor(1);
oled.setCursor(0, 0);
oled.println("FAILED");
oled.display();
for(;;);
} else {
oled.clearDisplay();
oled.setTextSize(1);
oled.setTextColor(1);
oled.setCursor(0, 0);
oled.println("SUCCESS");
oled.display();
Serial.println("SUCCESS");
}
pox.setOnBeatDetectedCallback(onBeatDetected);
}

في الدالة ()loop سيتم قراءة معدل ضربات القلب ونسبة الأكسجين في الدم وطباعتها على الشاشة OLED وإرسال البيانات عن طريق البلوتوث إلى جهاز الاندرويد.

سيتم تحديث البيانات كل ثانية لكي يتم أخذ القراءات أولًا بأول.

void loop()
{
pox.update();

if (millis() - tsLastReport > REPORTING_PERIOD_MS) {
Serial.print("Heart BPM:");
Serial.print(pox.getHeartRate());
Serial.print("-----");
Serial.print("Oxygen Percent:");
Serial.print(pox.getSpO2());
Serial.println("\n");
oled.clearDisplay();
oled.setTextSize(1);
oled.setTextColor(1);
oled.setCursor(0,16);
oled.println(pox.getHeartRate());

oled.setTextSize(1);
oled.setTextColor(1);
oled.setCursor(0, 0);
oled.println("Heart BPM");

oled.setTextSize(1);
oled.setTextColor(1);
oled.setCursor(0, 30);
oled.println("Spo2");

oled.setTextSize(1);
oled.setTextColor(1);
oled.setCursor(0,45);
oled.println(pox.getSpO2());
oled.display();
tsLastReport = millis();
}
}

برنامج MIT App Inventor

حمّل تصميم واجهة المستخدم والكود البرمجي من هنا.

من المتصفح افتح موقع MIT App Inventor.

أنشئ حساب على الموقع ثم انقر علي Create Apps.

arduino-blood-oxygen-heart-rate-monitor-max30100

من قائمة Project اختر Import projects (.aia) from my computer ثم انقر على الملف الذي حملته مسبقًا ويحتوي على واجهة المستخدم والكود البرمجي.

arduino-blood-oxygen-heart-rate-monitor-max30100

من قائمة Connect اختر Al companion

arduino-blood-oxygen-heart-rate-monitor-max30100

سيظهر كود ورمز مكون من 6 حروف.

حمّل تطبيق MIT App Inventor على جهازك الذكي.

يمكنك ادخال الرمز في المستطيل أو النقر على scan QR code ومسح الكود السابق.

ثم انقر على connect with code.
arduino-blood-oxygen-heart-rate-monitor-max30100

ستظهر هذه الواجهة انقر على Tap to connect.arduino-blood-oxygen-heart-rate-monitor-max30100

اختر موديول البلوتوث المستخدم هنا استخدمنا النوع HC-06.

نظام طبي

ستتم عملية الاقتران وستبدأ البيانات المقروءة من المستشعر بالظهور على الجهاز.نظام طبي

الآن تعلمت كيف تصنع نظام طبي يمكنك اختباره وذلك بوضع اصبعك على الحساس ومراقبة معدل نبضات القلب ونسبة الأكسجين.

 لا تنسَ فصل مصدر الطاقة بعد الانتهاء من استخدام النظام.

المشاكل والإصلاحات

إذا كان توصيلك صحيح والحساس لا يعمل، ببساطة قم بإزالة المقاومات ذات القيمة 1.8ohm ليتم استبدالها بمقاوامات ذات قيمة 4.7k ohm .

arduino-blood-oxygen-heart-rate-monitor-max30100

 

 

 

 




إنشاء خادم ويب باستخدام الاردوينو ووحدة ESP8266

مقدمة

في هذا الدرس سنتعلم كيفية إنشاء خادم ويب باستخدام الاردوينو ووحدة ESP8266.sending-arduino-data-to-webpage

المواد والأدوات

 خادم الويب

1× اردوينو اونو

 خادم الويب

1× سلك الاردوينو

sending-arduino-data-to-webpage

حزمة أسلاك توصيل (ذكر – أنثى)

ذكر-ذكر

 حزمة أسلاك توصيل (ذكر- ذكر)

WiFi Module ESP8266

وحدة Wi-Fi

لوحة تجارب

1× لوحة تجارب حجم صغير

 

توصيل الدائرة

وصل الدائرة الكهربائية كما في الشكل المقابل:خادم الويب

تهيئة Arduino IDE

  • افتح ِArduino IDE، من قائمة File اختر Preference.

sending-arduino-data-to-webpage

  • حمّل الملف الخاص بوحدة ESP8266 ثم انقر على Ok.

https://arduino.esp8266.com/stable/package_esp8266com_index.json

sending-arduino-data-to-webpage

  • من قائمة Tools اختر Board ثم Board manger.

خادم الويب

  • اكتب في خانة البحث ESP8266 حمّل اللوحة التالية آخر إصدار.

خادم الويب

الكود البرمجي

قبل رفع الكود البرمجي للوحة الاردوينو عليك قراءة شرح الكود البرمجي.

#include<SoftwareSerial.h>

SoftwareSerial client(2,3); //RX, TX

String webpage="";

int i=0,k=0;

String readString;
int x=0;
boolean No_IP=false;
String IP="";

char temp1='0';
String name="<p>Geeks valley</p>";   //22
String dat="<p>Data Received Successfully.....</p>";     //21


void check4IP(int t1)
{
int t2=millis();
while(t2+t1>millis())
 {
while(client.available()>0)
{
if(client.find("WIFI GOT IP"))
{
No_IP=true;
}
}

}
}

void get_ip()
{
 IP="";
char ch=0;
while(1)
{
client.println("AT+CIFSR");
while(client.available()>0)
{
if(client.find("STAIP,"))
{
delay(1000);
Serial.print("IP Address:");
while(client.available()>0)
 {
ch=client.read();
  if(ch=='+')
break;
 IP+=ch;
}
}
 if(ch=='+')
break;
 }
 if(ch=='+')
break;
delay(1000);
}
Serial.print(IP);
Serial.print("Port:");
Serial.println(80);
}

void connect_wifi(String cmd, int t)
{
 int temp=0,i=0;
 while(1)
{
Serial.println(cmd);
client.println(cmd);
while(client.available())
 {
if(client.find("OK"))
i=8;
}
delay(t);
  if(i>5)
break;
i++;
}
if(i==8)
Serial.println("OK");
else
Serial.println("Error");
}
void wifi_init()
{
connect_wifi("AT",100);
connect_wifi("AT+CWMODE=3",100);
connect_wifi("AT+CWQAP",100);  
connect_wifi("AT+RST",5000);
check4IP(5000);
 if(!No_IP)
 {
Serial.println("Connecting Wifi....");
 connect_wifi("AT+CWJAP=\"Network name\",\"Network password\"",7000);         //provide your WiFi username and password here
// connect_wifi("AT+CWJAP=\"vpn address\",\"wireless network\"",7000);
}
else
{
 }
Serial.println("Wifi Connected");
get_ip();
connect_wifi("AT+CIPMUX=1",100);
connect_wifi("AT+CIPSERVER=1,80",100);
}
void sendwebdata(String webPage)
{
 int ii=0;
while(1)
{
unsigned int l=webPage.length();
Serial.print("AT+CIPSEND=0,");
client.print("AT+CIPSEND=0,");
Serial.println(l+2);
client.println(l+2);
delay(100);
Serial.println(webPage);
client.println(webPage);
while(client.available())
{
 //Serial.print(Serial.read());
 if(client.find("OK"))
 {
ii=11;
break;
}
 }
 if(ii==11)
break;
delay(100);
  }
}
void setup()
{
Serial.begin(115200);
client.begin(115200);
wifi_init();


Serial.println("System Ready..");

}
void loop()

{
k=0;
Serial.println("Please Refresh your Page");
while(k<1000)
 {
 k++;
while(client.available())
{
 if(client.find("0,CONNECT"))
{
Serial.println("Start Printing");
 Send();
 Serial.println("Done Printing");
delay(1000);
}
 }
delay(1);
}

}
void Send()
{
webpage = "<h1>Welcome to Geeks valley</h1><body bgcolor=f0f0f0>";
sendwebdata(webpage);
webpage=name;
webpage+=dat;
sendwebdata(webpage);
delay(1000);
webpage = "<a href=\"https://geeksvalley.com/tutorials/";
webpage+="\">Click Here for tutorials</a>";
sendwebdata(webpage);
client.println("AT+CIPCLOSE=0");

}

شرح الكود البرمجي

نعرف المكتبات المستخدمة في المشروع وهي مكتبة الاتصال التسلسلي.

#include<SoftwareSerial.h>

هذا السطر يوضح المنافذ المستخدمة لربط Rx وTx.

SoftwareSerial client(2,3); //RX, TX

المتغير IP يحمل عنوان الشبكة للوحة Wi-Fi.

String IP="";

المتغير name يحمل جملة Geeks valley بتنسيق لغة HTML.

والمتغير date يحمل الجملة Data Received Successfully…..

String name="<p>Geeks valley</p>";   //22
String dat="<p>Data Received Successfully.....</p>";     //21

في الدالة check4IP سيتم البحث عن عنوان الشبكة.

void check4IP(int t1)
{
int t2=millis();
while(t2+t1>millis())
 {
while(client.available()>0)
{
if(client.find("WIFI GOT IP"))
{
No_IP=true;
}
}

}
}

في الدالة get_ip سيتم طباعة عنوان الشبكة IP address.

void get_ip()
{
 IP="";
char ch=0;
while(1)
{
client.println("AT+CIFSR");
while(client.available()>0)
{
if(client.find("STAIP,"))
{
delay(1000);
Serial.print("IP Address:");
while(client.available()>0)
 {
ch=client.read();
  if(ch=='+')
break;
 IP+=ch;
}
}
 if(ch=='+')
break;
 }
 if(ch=='+')
break;
delay(1000);
}
Serial.print(IP);
Serial.print("Port:");
Serial.println(80);
}

في الدالة wifi-init سيتم البحث عن الشبكات الموجودة في النطاق، ثم سيتم اختيار الشبكة التي قمت بتعيينها في الكود البرمجي.

void wifi_init()
{
connect_wifi("AT",100);
connect_wifi("AT+CWMODE=3",100);
connect_wifi("AT+CWQAP",100);  
connect_wifi("AT+RST",5000);
check4IP(5000);
 if(!No_IP)
 {
Serial.println("Connecting Wifi....");
 connect_wifi("AT+CWJAP=\"Network name\",\"network password\"",7000);         //provide your WiFi username and password here
// connect_wifi("AT+CWJAP=\"vpn address\",\"wireless network\"",7000);
else
{
 }
Serial.println("Wifi Connected");
get_ip();
connect_wifi("AT+CIPMUX=1",100);
connect_wifi("AT+CIPSERVER=1,80",100);
}

عليك تحرير السطر التالي ووضع اسم شبكة الانترنت الخاصة بك ورمزها السري.

 connect_wifi("AT+CWJAP=\"Network name\",\"Network password\"",7000);         //provide your WiFi username and password here

في الدالة setup سيتم تهيئة شاشة الاتصال التسلسلي لطباعة التحديثات الخاصة بوحدة Wi-Fi.

عليك تغيير سرعة نقل البيانات كما في الشكل.

خادم الويب

voidsetup()
{
Serial.begin(115200);
client.begin(115200);
wifi_init();


Serial.println("System Ready..");

}

في الدالة send سيتم انشاء خادم ويب.

ستظهر الجمل بهذا الشكل:

Welcome to Geeks valley

Geeks valley

Data Received Successfully…..

Click Here for tutorials

يمكن تحرير الجمل الموجودة:

void Send()
{
webpage = "<h1>Welcome to Geeks valley</h1><body bgcolor=f0f0f0>";
sendwebdata(webpage);
webpage=name;
webpage+=dat;
sendwebdata(webpage);
delay(1000);
webpage = "<a href=\"https://geeksvalley.com/tutorials/";
webpage+="\">Click Here for tutorials</a>";
sendwebdata(webpage);
client.println("AT+CIPCLOSE=0");

}

بعد رفع الكود البرمجي افتح شاشة الاتصال التسلسي وانسخ IP address عنوان الشبكة.

sending-arduino-data-to-webpage

وضعه في المتصفح الذي تفضله، ستظهر صفحة الويب كما في الشكل.

خادم الويب

المشاكل والإصلاحات

من أبرز المشاكل التي تظهر في وحدة Wi-Fi هي عدم الاستجابة لأوامر AT، إما بسبب خطأ بتوصيل الدائرة الكهربائية أو بسبب مشاكل في الكود البرمجي أو الوحدة تحتاج إلى تحديث Firmware.

سنتعلم في هذا القسم كيفية تحديث Firmware.

1- توصيل الدائرة

وصل الدائرة الكهربائية كما في الشكل المقابل:sending-arduino-data-to-webpage

2- تحميل ESP flasher و Firmware

حمّل Flasher و Firmware.

فك الضغط عن كلا الملفين.

3- تثبيت Firmware

افتح ESP8266 Flasher.

انقر على Bin اختر ملف Ai-Thinker_ESP8266 الذي قمت بتحميله مسبقًا.

واكتب رقم المنفذ التسلسلي الذي تستخدمه.sending-arduino-data-to-webpage

اضغط زر التحكم في الدائرة الكهربائية ثم انقر على زر تحميل Download.

sending-arduino-data-to-webpage

سيبدأ التحميل مباشرة، انتظر حتى ينتهي وتظهر هذه الرسالة.

خادم الويب

4- الاختبار

ملاحظة: من الدائرة الكهربائية افصل السلك المربوط مع GPI00.

سنستخدم AT commands لاختبار اللوحة.

AT Commands عبارة عن أوامر تسمح باتصال وحدة Wi-Fi مع لوحة الاردوينو.

افتح اردوينو IDE.

ارفع الكود البرمجي التالي للوحة الاردوينو (قبل رفع الكود البرمجي للوحة افصل سلك Rest و 3v ثم أعدهم بعد رفع الكود البرمجي.).

void setup() {
// put your setup code here, to run once:
}
void loop() {
// put your main code here, to run repeatedly:
}

افتح شاشة الاتصال التسلسلي وغيّر سرعة النقل كما في الصورة.

خادم الويب

ابدأ بكتابة الأوامر التالية:

الأمر الأول: AT

يعتبر الأمر الأساسي الذي يختبر بدء تشغيل AT، إذا كان نظام AT يعمل بشكل صحيح ستظهر رسالة OK إذا لم يعمل ستظهر رسالة Error.خادم الويب

الأمر الثاني: AT+RST

هذا الأمر يعيد تشغيل وحدة ESP8266.

ستظهر رسالة ready في النهاية.

الأمر الثالث: AT+CWMODE=3

يستخدم هذا الأمر لضبط وضع WiFi للتشغيل.

ستظهر رسالة OK عند الانتهاء من الضبط.

خادم الويب

 الأمر الرابع: AT+CWLAP

سيظهر جميع شبكات الانترنت المتوفرة في النطاق.

الأمر الخامس: AT+CWJAP=”اسم الشبكة”,”كلمة المرور”

يستخدم هذا الأمر لربط وحدة Wi-Fi مع شبكة الانترنت.sending-arduino-data-to-webpage

الأمر السادس: AT+CIFSR

يوضع عنوان الشبكة IP Address.

sending-arduino-data-to-webpage

بعد اكتمال هذه الخطوات تصبح الوحدة جاهزة للاستخدام.

 




ساعة الكلمات باللغة العربية

في هذا الدرس سنقدم مشروع ساعة تعرض الوقت بالكلمات العربية، بحيث يتم قص كلمات وصف الساعة على لوحة أكليريك، و تضيء الكلمات حسب الوقت الحالي، الساعة تختلف بدقة 5 دقائق ، سيتم برمجة المشروع باستخدام الأردوينو، وتحديد الوقت عن طريق وحدة ساعة الوقت الفعلي.

المواد و الأدوات

اردوينو

X1 أردوينو

سلك اردوينو

X1 سلك الأردوينو

محول طاقة

X1 محول طاقة (5فولت -2 أمبير)

X1 وحدة الوقت الفعلي

أسلاك توصيل

مجموعة أسلاك توصيل (أنثى/ذكر)

أسلاك ذكر / ذكر

مجموعة أسلاك توصيل (ذكر / ذكر)

شريط اضاءة

X1 شريط ثنائي القطب يحتوي على (52 وحدة )

سلك لحام

X1 سلك لحام

كاوية لحام

X1 أداة لحام

بكرة سلك

X1 بكرة سلك

أكليريك أسود

X1 لوح أكليريك أٍسود (50*40*3)

لوح أكليريك

X2 لوح اكليريك أبيض نافذ للضوء(50*40*3)

لحام أكليرك

لحام أكليريك

قاطع الليزر

توصيل الدائرة

توصيل الدائرة

التصميم

يمكنك تحميل التصميم من خلال الرابط

أولا: تصميم الواجهة التي تحتوي على الكلمات باللغة العربية

-الجزء الأول من الساعة يحتوي على الوقت بالساعات من الواحدة حتى الثانية عشرة وبعدها الوقت بالدقائق و يفصلهما (و/ إلا ) ليتم قراءة الوقت بشكل صحيح

– يتم عرض التغير في الوقت كل 5 خمس دقائق

– عندما تكون الدقائق 25 ستكون الساعة (و النصف وإلا خمس دقائق)

-عندما تكون الدقائق 35 ستكون الساعة (و النصف وخمس دقائق)

الساعة 10:25

ثانيا: تصميم شبكة حتى تفصل بين الكلمات المتجاورة

ثالثا: يتم تصميم الصندوق الذي يجمع هذه المكونات

التصنيع

 قبل البدء بهذه الخطوة يجب أن التأكد من برمجة و ضبط وحدة الساعة بالانتقال إلى جزء البرمجة في هذا الدرس أو من خلال رابط درس استخدام DS3231 RTC Module مع الاردوينو ، وو كذلك التحقق من شريط الثنائي المشع للضوء
-توزيع الثنائي المشع للضوء كما هو موضح بالصورة

توزيع الثنائي المشع للضوء

توزيع وحدات الثنائي المشع للضوء

-تثبيت لوحة الأكليريك التي تسمح بنفاذ الضوء ثم لوحة الكلمات على السطح

 

البرمجة

أولا: التحقق من ضبط وحدة ساعة الوقت الفعلي و طباعة الوقت على شاشة الإتصال التسلسلي من خلال رفع الشفرة البرمجية التالية

#include <RTClib.h>
#include <Wire.h>
RTC_DS3231 rtc;
char t[32];
int dtminutes;
int dthours;
int minutes;
int hours;
void setup()

{
  Serial.begin(9600);
  Wire.begin();
  rtc.begin();
  rtc.adjust(DateTime(F(__DATE__),F(__TIME__)));
  //rtc.adjust(DateTime(2019, 1, 21, 5, 0, 0));
}
void loop()
{ DateTime now = rtc.now();
  sprintf(t, "%02d:%02d:%02d %02d/%02d/%02d", now.hour(), now.minute(), now.second(), now.day(), now.month(), now.year());  
  Serial.print(F("Date/Time: "));
  Serial.println(t);
  delay(1000);
}

ثانيا : التحقق من التحكم بشريط الثنائي المشع للضوء من خلال رفع الشفرة البرمجية التالية 

#include <FastLED.h>

#define LED_PIN     5
#define NUM_LEDS    64
#define BRIGHTNESS  140
#define LED_TYPE    WS2811
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];

CRGB Colors [13]{CRGB::Gray,CRGB::Orange,CRGB::Maroon,CRGB::Red,CRGB::Cyan,CRGB::Lime,CRGB::Green,CRGB::Purple,
                     CRGB::Yellow,CRGB::White,CRGB::Blue};

void setup() {
    delay( 3000 ); // power-up safety delay
    FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
    FastLED.setBrightness(  BRIGHTNESS );
    
}

void loop()
{
for (int j=0;j<13;j++)  
  for (int i=0;i<65;i++){
    leds[i]=  Colors[j];
    FastLED.show();
    delay(50);
    leds[i]=CHSV( 0 , 0, 0);
    FastLED.show();
  }
}

ثالثا: بعد التحقق من ضبط الساعة و عمل شريط الثنائي المشع للضوء، نرفع الشفرة البرمجية من خلال الأمر التالي

#include <FastLED.h>                      
#include <RTClib.h>
#include <Wire.h>
#define LED_PIN     5
#define NUM_LEDS    65
#define BRIGHTNESS  200
#define LED_TYPE    WS2811
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];
RTC_DS3231 rtc;
int minutes;
int hours;
CRGB Colors[11]{CRGB::Chartreuse,CRGB::Orange,CRGB::BlueViolet,CRGB::Red,CRGB::Cyan,CRGB::Pink,
CRGB::Green,CRGB::Purple,CRGB::Yellow,CRGB::White,CRGB::Blue};
void setup()
{
  delay( 3000 );
  FastLED.clear() ;
  Serial.begin(9600);
  Wire.begin();
  rtc.begin();
  rtc.adjust(DateTime(F(__DATE__),F(__TIME__)));
  FastLED.addLeds<WS2812, LED_PIN, RGB>(leds, NUM_LEDS);
    FastLED.setBrightness(  BRIGHTNESS );
  //rtc.adjust(DateTime(2019, 1, 21, 5, 0, 0));
}
void huors()
{
if (hours==1){
     //28-29-30-31
      leds[28] = CRGB::Black;
      leds[29] = CRGB::Black;
      leds[30] = CRGB::Black;
      leds[31] = CRGB::Black;
      //7-8-9
      leds[7] = CRGB::Blue;
      leds[8] = CRGB::Blue;
      leds[9] = CRGB::Blue;
      FastLED.show();
      }
    if (hours==2){
      //7-8-9
      leds[7] = CRGB::Black;
      leds[8] = CRGB::Black;
      leds[9] = CRGB::Black;
      //18-19
      leds[18] = CRGB::Blue;
      leds[19] = CRGB::Blue;
     FastLED.show();
    }

    if (hours==3){
      //18-19
      leds[18] = CRGB::Black;
      leds[19] = CRGB::Black;
      //4-5-6
      leds[4] = CRGB::Blue;
      leds[5] = CRGB::Blue;
      leds[6] = CRGB::Blue;
      FastLED.show();
    }

    if (hours==4){
       //4-5-6
      leds[4] = CRGB::Black;
      leds[5] = CRGB::Black;
      leds[6] = CRGB::Black;
      //35-36
      leds[35] = CRGB::Blue;
      leds[36] = CRGB::Blue;  
      FastLED.show();
    }
      if (hours==5){//20-21
      //35-36
      leds[35] = CRGB::Black;
      leds[36] = CRGB::Black;
      //20-21
      leds[20] = CRGB::Blue;
      leds[21] = CRGB::Blue;
      FastLED.show();
      }
    if (hours==6){
      //20-21
      leds[20] = CRGB::Black;
      leds[21] = CRGB::Black;
      //32-33-34
      leds[32] = CRGB::Blue;
      leds[33] = CRGB::Blue;
      leds[34] = CRGB::Blue;
     FastLED.show();
    }
    if (hours==7){
      //32-33-34
      leds[32] = CRGB::Black;
      leds[33] = CRGB::Black;
      leds[34] = CRGB::Black;
      //24-25
      leds[24] = CRGB::Blue;
      leds[25] = CRGB::Blue;
      FastLED.show();
    }
    if (hours==8){
     //24-25
      leds[24] = CRGB::Black;
      leds[25] = CRGB::Black;
      //22-23
      leds[22] = CRGB::Blue;
      leds[23] = CRGB::Blue;
      FastLED.show();
    }
     if (hours==9){
      //22-23
      leds[22] = CRGB::Black;
      leds[23] = CRGB::Black;
      //26-27
      leds[26] = CRGB::Blue;
      leds[27] = CRGB::Blue;
      FastLED.show();
      }
    if (hours==10){
      leds[26] = CRGB::Black;
      leds[27] = CRGB::Black;
      //15-16-17
      leds[15] = CRGB::Blue;
      leds[16] = CRGB::Blue;
      leds[17] = CRGB::Blue;
     FastLED.show();
    }
    if (hours==11){
      //15-16-17
      leds[15] = CRGB::Black;
      leds[16] = CRGB::Black;
      leds[17] = CRGB::Black;
      //10-11-12-13-14
      leds[10] = CRGB::Blue;
      leds[11] = CRGB::Blue;
      leds[12] = CRGB::Blue;
      leds[13] = CRGB::Blue;
      leds[14] = CRGB::Blue;
      FastLED.show();
    }
    if (hours==12){
            //10-11-12-13-14
      leds[10] = CRGB::Black;
      leds[11] = CRGB::Black;
      leds[12] = CRGB::Black;
      leds[13] = CRGB::Black;
      leds[14] = CRGB::Black;
      //28-29-30-31
      leds[28] = CRGB::Blue;
      leds[29] = CRGB::Blue;
      leds[30] = CRGB::Blue;
      leds[31] = CRGB::Blue;
     FastLED.show();
    } }
    void Topast(){
      if (minutes){
      leds[26] = CRGB::Black;
      leds[27] = CRGB::Black;
      //15-16-17
      leds[15] = CRGB::Blue;
      leds[16] = CRGB::Blue;
      leds[17] = CRGB::Blue;
     FastLED.show();
    }
}

void mints(){
if (minutes==0){
      leds[43] = CRGB::Black;
      leds[51] = CRGB::Black;
      leds[52] = CRGB::Black;
}

if ((minutes==5)  or (minutes==55)){
      leds[43] = CRGB::Blue;
      leds[51] = CRGB::Blue;
      leds[52] = CRGB::Blue;
      leds[48] = CRGB::Black;
}
if ((minutes==10)  or (minutes==50)){
  leds[43] = CRGB::Black;
  leds[42] = CRGB::Black;
  leds[48] = CRGB::Blue;
  leds[51] = CRGB::Blue;
  leds[52] = CRGB::Blue;
}
if ((minutes==15)  or (minutes==45)){
  leds[42] = CRGB::Blue;
  leds[48] = CRGB::Black;
  leds[44] = CRGB::Black;
  leds[51] = CRGB::Black;
  leds[52] = CRGB::Black;
}
if ((minutes==20)  or (minutes==40)){
    leds[42] = CRGB::Black;
    leds[44] = CRGB::Blue;
    leds[51] = CRGB::Black;
    leds[52] = CRGB::Black; 
    leds[38] = CRGB::Black;
    leds[39] = CRGB::Black;
     leds[40] = CRGB::Black;
    leds[41] = CRGB::Black;
}
if (minutes==30) {
   
    leds[46] = CRGB::Black;
    leds[47] = CRGB::Black;
    leds[38] = CRGB::Black;
    leds[39] = CRGB::Black;
    leds[51] = CRGB::Black;
    leds[52] = CRGB::Black;
    leds[49] = CRGB::Blue;
    leds[50] = CRGB::Blue;
}
if (minutes==25) {
    leds[44] = CRGB::Black;
    leds[38] = CRGB::Blue;
    leds[39] = CRGB::Blue;
    leds[46] = CRGB::Blue;
    leds[47] = CRGB::Blue;
    leds[51] = CRGB::Blue;
    leds[52] = CRGB::Blue;
}

if (minutes==35) {
leds[38] = CRGB::Blue;
    leds[39] = CRGB::Blue;
    leds[49] = CRGB::Black;
    leds[50] = CRGB::Black;
    leds[40] = CRGB::Blue;
    leds[41] = CRGB::Blue;
    leds[51] = CRGB::Blue;
    leds[52] = CRGB::Blue;
}
}
void pastto(){
  if (minutes=35 and minutes>=5) {
  leds[37] = CRGB::Blue;
  leds[45] = CRGB::Black;}
  if (minutes<=55 and minutes>=40) {
  leds[45] = CRGB::Blue;
  leds[37] = CRGB::Black;}
if (minutes=0) {
  leds[45] = CRGB::Black; }
}
void loop()
{  
  leds[0] = CRGB::White;
  leds[1] = CRGB::White;
  leds[2] = CRGB::White;
  leds[3] = CRGB::White;
  DateTime now = rtc.now();
   minutes = ("%02d", now.minute() );
    minutes=(minutes/5)*5;
   hours = ("%02d", now.hour() );
 if (hours>12){
  hours=hours-12;
  if (minutes>35 and minutes<=55)
  hours=hours+1;}
  mints();
//Serial.print (hours);
//Serial.print (":");
//Serial.println (minutes);
delay(300);
huors();
pastto();
}

شرح البرمجة 

نبدأ بتحميل المكتبات شرح طريقة تحميل المكتبات من خلال الرابط

#include <FastLED.h>                     
#include <RTClib.h>
#include <Wire.h>

نعرف المتغيرات قيمة كل متغير ظاهرة حسب اسم المتغير

#define LED_PIN     5
#define NUM_LEDS    65
#define BRIGHTNESS  200
#define LED_TYPE    WS2811
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];
RTC_DS3231 rtc;
int minutes;
int hours;
CRGB Colors[11]{CRGB::Chartreuse,CRGB::Orange,CRGB::BlueViolet,CRGB::Red,CRGB::Cyan,CRGB::Pink,
CRGB::Green,CRGB::Purple,CRGB::Yellow,CRGB::White,CRGB::Blue};

في دالة (setup) نكتب أوامر التهيئة الرئيسية الخاصة بشريط الثنائي المشع للضوء و وحدة الوقت الفعلي

void setup()
{
delay( 3000 );
FastLED.clear() ;
Serial.begin(9600);
Wire.begin();
rtc.begin();
rtc.adjust(DateTime(F(__DATE__),F(__TIME__)));
FastLED.addLeds<WS2812, LED_PIN, RGB>(leds, NUM_LEDS);
  FastLED.setBrightness(  BRIGHTNESS );
//rtc.adjust(DateTime(2019, 1, 21, 5, 0, 0));
}

سنقوم بعمل ثلاث دوال واحدة تعمل على التحكم بثنائي المشع للضوء الخاص بالساعات
وواحدة للتحكم بثنائي المشع للضوء الخاص بالدقائق
وواحدة للتحكم بثنائي المشع للضوء الخاص بكل من ( إلا / و)

void huors()
{
if (hours==1){
   //28-29-30-31
    leds[28] = CRGB::Black;
    leds[29] = CRGB::Black;
    leds[30] = CRGB::Black;
    leds[31] = CRGB::Black;
    //7-8-9
    leds[7] = CRGB::Blue;
    leds[8] = CRGB::Blue;
    leds[9] = CRGB::Blue;
    FastLED.show();
    }
  if (hours==2){
    //7-8-9
    leds[7] = CRGB::Black;
    leds[8] = CRGB::Black;
    leds[9] = CRGB::Black;
    //18-19
    leds[18] = CRGB::Blue;
    leds[19] = CRGB::Blue;
   FastLED.show();
    }

  if (hours==3){
    //18-19
    leds[18] = CRGB::Black;
    leds[19] = CRGB::Black;
    //4-5-6
    leds[4] = CRGB::Blue;
    leds[5] = CRGB::Blue;
    leds[6] = CRGB::Blue;
    FastLED.show();
    }

  if (hours==4){
     //4-5-6
    leds[4] = CRGB::Black;
    leds[5] = CRGB::Black;
    leds[6] = CRGB::Black;
    //35-36
    leds[35] = CRGB::Blue;
    leds[36] = CRGB::Blue;  
    FastLED.show();
  }
    if (hours==5){//20-21
    //35-36
    leds[35] = CRGB::Black;
    leds[36] = CRGB::Black;
    //20-21
    leds[20] = CRGB::Blue;
    leds[21] = CRGB::Blue;
    FastLED.show();
    }
  if (hours==6){
    //20-21
    leds[20] = CRGB::Black;
    leds[21] = CRGB::Black;
    //32-33-34
    leds[32] = CRGB::Blue;
    leds[33] = CRGB::Blue;
    leds[34] = CRGB::Blue;
   FastLED.show();
  }
  if (hours==7){
    //32-33-34
    leds[32] = CRGB::Black;
    leds[33] = CRGB::Black;
    leds[34] = CRGB::Black;
    //24-25
    leds[24] = CRGB::Blue;
    leds[25] = CRGB::Blue;
    FastLED.show();
  }
  if (hours==8){
  //24-25
    leds[24] = CRGB::Black;
    leds[25] = CRGB::Black;
    //22-23
    leds[22] = CRGB::Blue;
    leds[23] = CRGB::Blue;
    FastLED.show();
  }
   if (hours==9){
    //22-23
    leds[22] = CRGB::Black;
    leds[23] = CRGB::Black;
    //26-27
    leds[26] = CRGB::Blue;
    leds[27] = CRGB::Blue;
    FastLED.show();
    }
  if (hours==10){
    leds[26] = CRGB::Black;
    leds[27] = CRGB::Black;
      //15-16-17
    leds[15] = CRGB::Blue;
    leds[16] = CRGB::Blue;
    leds[17] = CRGB::Blue;
   FastLED.show();
  }
  if (hours==11){
    //15-16-17
    leds[15] = CRGB::Black;
    leds[16] = CRGB::Black;
    leds[17] = CRGB::Black;
    //10-11-12-13-14
    leds[10] = CRGB::Blue;
    leds[11] = CRGB::Blue;
    leds[12] = CRGB::Blue;
    leds[13] = CRGB::Blue;
    leds[14] = CRGB::Blue;
    FastLED.show();
  }
  if (hours==12){
          //10-11-12-13-14
    leds[10] = CRGB::Black;
    leds[11] = CRGB::Black;
    leds[12] = CRGB::Black;
    leds[13] = CRGB::Black;
    leds[14] = CRGB::Black;
    //28-29-30-31
    leds[28] = CRGB::Blue;
    leds[29] = CRGB::Blue;
    leds[30] = CRGB::Blue;
    leds[31] = CRGB::Blue;
   FastLED.show();
  } }
  void Topast(){
    if (minutes){
    leds[26] = CRGB::Black;
    leds[27] = CRGB::Black;
    //15-16-17
    leds[15] = CRGB::Blue;
    leds[16] = CRGB::Blue;
    leds[17] = CRGB::Blue;
   FastLED.show();
  }
}

void mints(){
if (minutes==0){
    leds[43] = CRGB::Black;
    leds[51] = CRGB::Black;
    leds[52] = CRGB::Black;
}

if ((minutes==5)  or (minutes==55)){
    leds[43] = CRGB::Blue;
    leds[51] = CRGB::Blue;
    leds[52] = CRGB::Blue;
    leds[48] = CRGB::Black;
}
if ((minutes==10)  or (minutes==50)){
leds[43] = CRGB::Black;
leds[42] = CRGB::Black;
leds[48] = CRGB::Blue;
leds[51] = CRGB::Blue;
leds[52] = CRGB::Blue;
}
if ((minutes==15)  or (minutes==45)){
leds[42] = CRGB::Blue;
leds[48] = CRGB::Black;
leds[44] = CRGB::Black;
leds[51] = CRGB::Black;
leds[52] = CRGB::Black;
}
if ((minutes==20)  or (minutes==40)){
  leds[42] = CRGB::Black;
  leds[44] = CRGB::Blue;
  leds[51] = CRGB::Black;
  leds[52] = CRGB::Black; 
   leds[38] = CRGB::Black;
  leds[39] = CRGB::Black;
   leds[40] = CRGB::Black;
  leds[41] = CRGB::Black;
}
if (minutes==30) {
   leds[46] = CRGB::Black;
  leds[47] = CRGB::Black;
  leds[38] = CRGB::Black;
  leds[39] = CRGB::Black;
  leds[51] = CRGB::Black;
  leds[52] = CRGB::Black;
  leds[49] = CRGB::Blue;
  leds[50] = CRGB::Blue;
}
if (minutes==25) {
  leds[44] = CRGB::Black;
  leds[38] = CRGB::Blue;
  leds[39] = CRGB::Blue;
  leds[46] = CRGB::Blue;
  leds[47] = CRGB::Blue;
  leds[51] = CRGB::Blue;
  leds[52] = CRGB::Blue;
}

if (minutes==35) {
leds[38] = CRGB::Blue;
  leds[39] = CRGB::Blue;
  leds[49] = CRGB::Black;
  leds[50] = CRGB::Black;
  leds[40] = CRGB::Blue;
  leds[41] = CRGB::Blue;
  leds[51] = CRGB::Blue;
  leds[52] = CRGB::Blue;
}
}
void pastto(){
if (minutes=35 and minutes>=5) {
leds[37] = CRGB::Blue;
leds[45] = CRGB::Black;}
if (minutes<=55 and minutes>=40) {
leds[45] = CRGB::Blue;
leds[37] = CRGB::Black;}
if (minutes=0) {
leds[45] = CRGB::Black; }
}

في دالة (loop) سنعرف قيمة الوقت و نجري عمليات رياضية كالتالي
-تغير الدقائق لأحد مضاعفات الخمسة
-قراءة الساعة بصيغة اثنى عشر ساعة
-زيادة قيمة الساعة بواحد عندما تكون الدقائق أكبر من 35

استدعاء الدوال الثلاثة الخاصة بالتحكم بالثنائي المشع للضوء

void loop()
{ 
leds[0] = CRGB::White;
leds[1] = CRGB::White;
leds[2] = CRGB::White;
leds[3] = CRGB::White;
DateTime now = rtc.now();
 minutes = ("%02d", now.minute() );
  minutes=(minutes/5)*5;
 hours = ("%02d", now.hour() );
if (hours>12){
hours=hours-12;
if (minutes>35 and minutes<=55)
hours=hours+1;}
mints();
//Serial.print (hours);
//Serial.print (":");
//Serial.println (minutes);
delay(300);
huors();
pastto();
}

 

ساعة بالكلمات العربية




محول الأنظمة العددية باستخدام الاردوينو

مقدمة

في هذا الدرس سنتعلم طريقة برمجة محول الأنظمة العددية باستخدام الاردوينو ولوحة المفاتيح 4*4. بحيث يتمكن المستخدم من كتابة الرقم ثم تحويله إلى أي قيمة عددية آخرى وذلك بالنقر على الأحرف A و B و C و D.

الأنظمة العددية

الأنظمة العددية

الأنظمة العددية تحوّل الأرقام المتعارف عليها إلى صيغ فريدة ومختلفة وذلك عن طريق استخدام أرقام أخرى أو مجموعة حروف أو رموز بطريقة متناسقة.

1- النظام العشري: هو النظام المستخدم فى الحياة اليومية والأساس له 10
2- النظام الثنائي: هو النظام المستخدم في أجهزة الحاسب وهو يكون 0 أو 1 والأساس له 2
3- النظام الثماني: هو النظام يكون 0 1 2 3 4 5 6 7  والأساس له 8
4- النظام السادس عشر: هو النظام يكون 0 1 2 3 4 5 6 7 8 9 F E D C B A والأساس له 16

الأنظمة العددية

المواد والأدوات

number-conversion-by-arduino

1× اردوينو اونو

number-conversion-by-arduino

1× سلك الاردوينو

number-conversion-by-arduino

لوحة مفاتيح

مهاراتك في الرياضيات

 1× شاشة كرستالية

الأنظمة العددية

1× 40 رأس دبوس

الأنظمة العددية

 حزمة أسلاك توصيل (ذكر- ذكر)

الأنظمة العددية

حزمة أسلاك توصيل (ذكر – أنثى)

الأنظمة العددية

1× 2C / IIC Serial Interface Module

توصيل الدائرة

سنقوم بتوصيل الدائرة الكهربائية كما في الشكل:الأنظمة العددية

البرمجة

سنقوم برفع الكود البرمجي لمشروع محول الأنظمة العددية على لوحة الاردوينو باستخدام برنامج اردوينو IDE.

#include <LiquidCrystal_I2C.h>

LiquidCrystal_I2C lcd(0x27,20,4); // set the LCD address to 0x27 for a 16 chars and 2 line display


int NumberToBeDisplayed = 0;
int TempNum = 0;
/*Rows of keypad connected to D6-D9 of Arduino*/
int r1=6;
int r2=7;
int r3=8;
int r4=9;
/*Colums of keypad connected to D10-D13 of Arduino*/
int c1=10;
int c2=11;
int c3=12;
int c4=13;
/*Declared four variable to read colums status*/
int col1;
int col2;
int col3;
int col4;
void setup()
{
/*Declared Row pins as OUTPUT*/
pinMode(r1,OUTPUT);
pinMode(r2,OUTPUT);
pinMode(r3,OUTPUT);
pinMode(r4,OUTPUT);
/*Declared Column pins as OUTPUT*/
pinMode(c1,INPUT);
pinMode(c2,INPUT);
pinMode(c3,INPUT);
pinMode(c4,INPUT);
digitalWrite(c1,HIGH);
digitalWrite(c2,HIGH);
digitalWrite(c3,HIGH);
digitalWrite(c4,HIGH);
pinMode(4,OUTPUT);
/*lcd brightness terminal connected*/
digitalWrite(4,0);
lcd.begin();
lcd.setCursor(0,0);
lcd.print("A:Oct B:Binary");
lcd.setCursor(0,1);
lcd.print("C:Hex D:Decimal");
lcd.setCursor(0,1);
delay(2000);
lcd.clear();
lcd.setCursor(0,1);
}
void loop()
{
digitalWrite(r1,LOW);
digitalWrite(r2,HIGH);
digitalWrite(r3,HIGH);
digitalWrite(r4,HIGH);
col1=digitalRead(c1);
col2=digitalRead(c2);
col3=digitalRead(c3);
col4=digitalRead(c4);
if(col1==LOW)
{
lcd.print("1");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 1;
while(digitalRead(c1) == LOW);
}
else if(col2==LOW)
{
lcd.print("2");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 2;
while(digitalRead(c2) == LOW);
}
else if(col3==LOW)
{
lcd.print("3");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 3;
while(digitalRead(c3) == LOW);
}
else if(col4==0)
{
lcd.clear();
lcd.print("—- OCTAL —–");
lcd.setCursor(0,1);
TempNum = ConvertDecimalToBase(NumberToBeDisplayed,8);
lcd.print(TempNum);
while(digitalRead(c4) == LOW);
}
digitalWrite(r1,HIGH);
digitalWrite(r2,LOW);
digitalWrite(r3,HIGH);
digitalWrite(r4,HIGH);
col1=digitalRead(c1);
col2=digitalRead(c2);
col3=digitalRead(c3);
col4=digitalRead(c4);
if(col1==LOW)
{
lcd.print("4");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 4;
while(digitalRead(c1) == LOW);
}
else if(col2==LOW)
{
lcd.print("5");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 5;
while(digitalRead(c2) == LOW);
}
else if(col3==LOW)
{
lcd.print("6");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 6;
while(digitalRead(c3) == LOW);
}
else if(col4==LOW)
{
delay(50);
lcd.clear();
lcd.print("—- BINARY —-");
lcd.setCursor(0,1);
for(int i=15 ; i>=0 ;i--)
{
lcd.print((NumberToBeDisplayed >> i & 1));
}
while(digitalRead(c4) == LOW);
}
digitalWrite(r1,HIGH);
digitalWrite(r2,HIGH);
digitalWrite(r3,LOW);
digitalWrite(r4,HIGH);
col1=digitalRead(c1);
col2=digitalRead(c2);
col3=digitalRead(c3);
col4=digitalRead(c4);
if(col1==LOW)
{
lcd.print("7");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 7;
while(digitalRead(c1) == LOW);
}
else if(col2==LOW)
{
lcd.print("8");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 8;
while(digitalRead(c2) == LOW);
}
else if(col3==LOW)
{
lcd.print("9");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 9;
while(digitalRead(c3) == LOW);
}
else if(col4==LOW)
{
delay(50);
lcd.clear();
lcd.print("- HEXADECIMAL –");
lcd.setCursor(0,1);
TempNum = NumberToBeDisplayed;
ConvertDecimalToHex(TempNum);
while(digitalRead(c4) == LOW);
}
digitalWrite(r1,HIGH);
digitalWrite(r2,HIGH);
digitalWrite(r3,HIGH);
digitalWrite(r4,LOW);
col1=digitalRead(c1);
col2=digitalRead(c2);
col3=digitalRead(c3);
col4=digitalRead(c4);
if(col1==LOW)
{
delay(50);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("A:Oct B:Binary");
lcd.setCursor(0,1);
lcd.print(":Hex D:Decimal");
lcd.setCursor(0,1);
delay(2000);
lcd.clear();
NumberToBeDisplayed = 0;
while(digitalRead(c2) == LOW);
}
else if(col2==LOW)
{
lcd.print("0");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 0;
while(digitalRead(c2) == LOW);
}
else if(col3==LOW)
{
delay(50);
lcd.clear();
lcd.print("—- OCTAL —–");
lcd.setCursor(0,1);
TempNum = ConvertDecimalToBase(NumberToBeDisplayed,8);
lcd.print(TempNum);
delay(1000);
lcd.clear();
lcd.print("—- BINARY —-");
lcd.setCursor(0,1);
for(int i=15 ; i>=0 ; i--)
{
lcd.print((NumberToBeDisplayed >> i & 1));
}
delay(1000);
lcd.clear();
lcd.print("- HEXADECIMAL –");
lcd.setCursor(0,1);
TempNum = NumberToBeDisplayed;
ConvertDecimalToHex(TempNum);
delay(1000);
lcd.clear();
lcd.print("— DECIMAL —-");
lcd.setCursor(0,1);
lcd.print(NumberToBeDisplayed);
delay(1000);
lcd.clear();
NumberToBeDisplayed = 0;
lcd.setCursor(0,0);
lcd.print("A:Oct B:Binary");
lcd.setCursor(0,1);
lcd.print("C:Hex D:Decimal");
lcd.setCursor(0,1);
delay(2000);
lcd.clear();
lcd.setCursor(0,1);
while(digitalRead(c3) == LOW);
}
else if(col4==LOW)
{
delay(50);
lcd.clear();
lcd.print("— DECIMAL —-");
lcd.setCursor(0,1);
lcd.print(NumberToBeDisplayed);
while(digitalRead(c4) == LOW);
}
}
int ConvertDecimalToBase(int n, int b)
{
int r=0, digitPos=1;
while (n)
{
r += (n%b)*digitPos;
n /= b;
digitPos *= 10;
}
return r;
}
void ConvertDecimalToHex(long int num)
{
long int rem[50],i=0,length=0;
while(num>0)
{
rem[i]=num%16;
num=num/16;
i++;
length++;
}
for(i=length-1 ; i>=0 ; i--)
{
switch(rem[i])
{
case 10:
lcd.print("A");
break;
case 11:
lcd.print("B");
break;
case 12:
lcd.print("C");
break;
case 13:
lcd.print("D");
break;
case 14:
lcd.print("E");
break;
case 15:
lcd.print("F");
break;
default :
lcd.print(rem[i]);
}
}
}

شرح الكود البرمجي

سنقوم في البداية باستدعاء مكتبة (LiquidCrystal_I2C.h) الخاصة بوحدة i2c والتي تحتوي على مجموعة أوامر برمجية نحتاجها في مشروع تحديد الاتجاهات.

ثم نضيفها للاردوينو IDE.

بتتبع المسار التالي:

Sketch > Include libraries > Add ZIP library 

ونضيف المجلد الذي قمنا بتحميله.

#include <LiquidCrystal_I2C.h>

نعرف عنوان وحدة i2c.

LiquidCrystal_I2C lcd(0x27,20,4); // set the LCD address to 0x27 for a 16 chars and 2 line display

سيتم تعيين القيمة 0 للمتغير NumberToBeDisplayed.

int NumberToBeDisplayed = 0;

هنا يتم إيضاح طريقة ربط منافذ لوحة المفاتيح مع منافذ لوحة الاردوينو الرقمية.

/*Rows of keypad connected to D6-D9 of Arduino*/
int r1=6;
int r2=7;
int r3=8;
int r4=9;
/*Colums of keypad connected to D10-D13 of Arduino*/
int c1=10;
int c2=11;
int c3=12;
int c4=13;
/*Declared four variable to read colums status*/
int col1;
int col2;
int col3;
int col4;

 في الدالة Setup يتم تهيئة الشاشة الكرستالية استعدادًا لطباعة القيم عليها.

كما يتم تهيئة منافذ لوحة المفاتيح r1, r2,r3,r4, c1,c2,c3,c4.

void setup()
{
/*Declared Row pins as OUTPUT*/
pinMode(r1,OUTPUT);
pinMode(r2,OUTPUT);
pinMode(r3,OUTPUT);
pinMode(r4,OUTPUT);
/*Declared Column pins as OUTPUT*/
pinMode(c1,INPUT);
pinMode(c2,INPUT);
pinMode(c3,INPUT);
pinMode(c4,INPUT);
digitalWrite(c1,HIGH);
digitalWrite(c2,HIGH);
digitalWrite(c3,HIGH);
digitalWrite(c4,HIGH);
pinMode(4,OUTPUT);
/*lcd brightness terminal connected*/
digitalWrite(4,0);
lcd.begin();
lcd.setCursor(0,0);
lcd.print("A:Oct B:Binary");
lcd.setCursor(0,1);
lcd.print("C:Hex D:Decimal");
lcd.setCursor(0,1);
delay(2000);
lcd.clear();
lcd.setCursor(0,1);
}

في الدالة Loop سيتم برمجة الشاشة الكرستالية لتحتوي في البداية على واجهة تعريفية للمستخدم.

A: OCT (النظام الثماني)

B: Binary (النظام الثنائي)

C: Hex (النظام السادس عشر)

D: Dec (النظام العشري)

بعد ذلك ستظهر واجهة فارغة للمستخدم يمكن من خلالها كتابة أي رقم يريده وبعد ذلك يختار ما بين الأنظمة العددية (A,B, D و C)

ثم سيقوم النظام مباشرة بالتحويل حسب المطلوب.

لمسح الشاشة والبدء من جديد اضغظ على علامة النجمة *.

void loop()
{
digitalWrite(r1,LOW);
digitalWrite(r2,HIGH);
digitalWrite(r3,HIGH);
digitalWrite(r4,HIGH);
col1=digitalRead(c1);
col2=digitalRead(c2);
col3=digitalRead(c3);
col4=digitalRead(c4);
if(col1==LOW)
{
lcd.print("1");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 1;
while(digitalRead(c1) == LOW);
}
else if(col2==LOW)
{
lcd.print("2");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 2;
while(digitalRead(c2) == LOW);
}
else if(col3==LOW)
{
lcd.print("3");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 3;
while(digitalRead(c3) == LOW);
}
else if(col4==0)
{
lcd.clear();
lcd.print("—- OCTAL —–");
lcd.setCursor(0,1);
TempNum = ConvertDecimalToBase(NumberToBeDisplayed,8);
lcd.print(TempNum);
while(digitalRead(c4) == LOW);
}
digitalWrite(r1,HIGH);
digitalWrite(r2,LOW);
digitalWrite(r3,HIGH);
digitalWrite(r4,HIGH);
col1=digitalRead(c1);
col2=digitalRead(c2);
col3=digitalRead(c3);
col4=digitalRead(c4);
if(col1==LOW)
{
lcd.print("4");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 4;
while(digitalRead(c1) == LOW);
}
else if(col2==LOW)
{
lcd.print("5");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 5;
while(digitalRead(c2) == LOW);
}
else if(col3==LOW)
{
lcd.print("6");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 6;
while(digitalRead(c3) == LOW);
}
else if(col4==LOW)
{
delay(50);
lcd.clear();
lcd.print("—- BINARY —-");
lcd.setCursor(0,1);
for(int i=15 ; i>=0 ;i--)
{
lcd.print((NumberToBeDisplayed >> i & 1));
}
while(digitalRead(c4) == LOW);
}
digitalWrite(r1,HIGH);
digitalWrite(r2,HIGH);
digitalWrite(r3,LOW);
digitalWrite(r4,HIGH);
col1=digitalRead(c1);
col2=digitalRead(c2);
col3=digitalRead(c3);
col4=digitalRead(c4);
if(col1==LOW)
{
lcd.print("7");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 7;
while(digitalRead(c1) == LOW);
}
else if(col2==LOW)
{
lcd.print("8");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 8;
while(digitalRead(c2) == LOW);
}
else if(col3==LOW)
{
lcd.print("9");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 9;
while(digitalRead(c3) == LOW);
}
else if(col4==LOW)
{
delay(50);
lcd.clear();
lcd.print("- HEXADECIMAL –");
lcd.setCursor(0,1);
TempNum = NumberToBeDisplayed;
ConvertDecimalToHex(TempNum);
while(digitalRead(c4) == LOW);
}
digitalWrite(r1,HIGH);
digitalWrite(r2,HIGH);
digitalWrite(r3,HIGH);
digitalWrite(r4,LOW);
col1=digitalRead(c1);
col2=digitalRead(c2);
col3=digitalRead(c3);
col4=digitalRead(c4);
if(col1==LOW)
{
delay(50);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("A:Oct B:Binary");
lcd.setCursor(0,1);
lcd.print(":Hex D:Decimal");
lcd.setCursor(0,1);
delay(2000);
lcd.clear();
NumberToBeDisplayed = 0;
while(digitalRead(c2) == LOW);
}
else if(col2==LOW)
{
lcd.print("0");
delay(50);
NumberToBeDisplayed = (NumberToBeDisplayed*10) + 0;
while(digitalRead(c2) == LOW);
}
else if(col3==LOW)
{
delay(50);
lcd.clear();
lcd.print("—- OCTAL —–");
lcd.setCursor(0,1);
TempNum = ConvertDecimalToBase(NumberToBeDisplayed,8);
lcd.print(TempNum);
delay(1000);
lcd.clear();
lcd.print("—- BINARY —-");
lcd.setCursor(0,1);
for(int i=15 ; i>=0 ; i--)
{
lcd.print((NumberToBeDisplayed >> i & 1));
}
delay(1000);
lcd.clear();
lcd.print("- HEXADECIMAL –");
lcd.setCursor(0,1);
TempNum = NumberToBeDisplayed;
ConvertDecimalToHex(TempNum);
delay(1000);
lcd.clear();
lcd.print("— DECIMAL —-");
lcd.setCursor(0,1);
lcd.print(NumberToBeDisplayed);
delay(1000);
lcd.clear();
NumberToBeDisplayed = 0;
lcd.setCursor(0,0);
lcd.print("A:Oct B:Binary");
lcd.setCursor(0,1);
lcd.print("C:Hex D:Decimal");
lcd.setCursor(0,1);
delay(2000);
lcd.clear();
lcd.setCursor(0,1);
while(digitalRead(c3) == LOW);
}
else if(col4==LOW)
{
delay(50);
lcd.clear();
lcd.print("— DECIMAL —-");
lcd.setCursor(0,1);
lcd.print(NumberToBeDisplayed);
while(digitalRead(c4) == LOW);
}
}
int ConvertDecimalToBase(int n, int b)
{
int r=0, digitPos=1;
while (n)
{
r += (n%b)*digitPos;
n /= b;
digitPos *= 10;
}
return r;
}
void ConvertDecimalToHex(long int num)
{
long int rem[50],i=0,length=0;
while(num>0)
{
rem[i]=num%16;
num=num/16;
i++;
length++;
}
for(i=length-1 ; i>=0 ; i--)
{
switch(rem[i])
{
case 10:
lcd.print("A");
break;
case 11:
lcd.print("B");
break;
case 12:
lcd.print("C");
break;
case 13:
lcd.print("D");
break;
case 14:
lcd.print("E");
break;
case 15:
lcd.print("F");
break;
default :
lcd.print(rem[i]);
}
}
}

لا تنسَ فصل مصدر الطاقة بعد الانتهاء من استخدام برمجة محول الأنظمة العددية باستخدام الاردوينو.

 




صنع جهاز للتحكم عن بعد بالروبوت باستخدام (NRF24L01)

تتطلب بعض المشاريع الإلكترونية، توصيل القطع لاسلكياً معاَ سواء لإرسال البيانات او استقبالها او إرسال إشارات للتحكم في تشغيل أو إغلاق القطع أو قراءة بيانات من الحساسات، في هذا الدرس سنتعرف على وحدة (NRF24L01) والتي سنعمل على برمجتها مع الأردوينو وعصا التحكم لصنع جهاز تحكم عن بعد بالروبوت.

NRF24L01

 

المواد و الأدوات

اردوينو

2X اردوينو أونو

سلك اردوينو

X1 سلك أردوينو

وحدة وحدة الارسال و الاستقبال (NRF24L01)

2X وحدة إرسال و استقبال (NRF24L01)

هيكل الروبوت

1X هيكل روبوت بأربع عجلات

1X عصا تحكم

لوحة تجارب صغيرة

1X لوحة تجارب

بطارية مع حامل بطارية

6X بطاريات (يفضل توفير حامل بطاريات لعدد 6)

l298

1X دائرة التحكم بالمحركات (L298)

أسلاك توصيل

أسلاك توصيل (ذكر/ أنثى)

أسلاك ذكر / ذكر

  أسلاك توصيل (ذكر/ ذكر)

 

توصيل الدائرة

بالبداية قم بالاطلاع على الدرس لمعرفة كيفة تثبيت هيكل الروبوت

أولا: توصيل دائرة المستقبل

دائرة المستقبل NRF24L01

توصيل المحركات :
L298H-bridge أسلاك المحركات
OUT  2 الأسلاك ذات اللون الأسود على جهة اليسار (-)
OUT 1 الأسلاك ذات اللون الأحمر على جهة اليسار (+)
OUT 4 الأسلاك ذات اللون الأسود على جهة اليمين (-)
OUT 3 الأسلاك ذات اللون الأحمر على جهة اليمين (+)
توصيل المنافذ الرقمية من الاردوينو مع  L298H-bridge :
L298H-bridge الأردوينو
enA 6
IN 1 7
IN 2 5
IN 3 4
IN4 2
enB 3

 

توصيل وحدة إرسال و استقبال (NRF24L01)

NRF24L01 الأردوينو
VCC 3.3
CSN 9
MOSI 11
GND GND
CE 8
SCK 13
MISO 12

ثانيا: توصيل دائرة المرسل

سيتم توصيل وحدة اللإرسال و الاستقبال (NRF24L01) بنفس الربط في المستقبل

دائرة المرسل NRF24L01

 

وحدة الارسال و الاستقبال (NRF24L01)

تسمح وحدة الإرسال والاستقبال nRF24L01 لمتحكمين أو أكثر بالتواصل مع بعضهما البعض لاسلكيًا وتعمل على بروتوكول SPI. ونطاق ترددها 2.4 جيجا هرتز ونطاق الاتصال يصل إلى 100 متر.

NRF24L01الجدول يوضح وظائف المنافذ للوحدة

المنفذ الوظيفة
VCC يستخدم هذا المنفذ لتزويد الوحدة بالطاقة. يمكن أن يتراوح الجهد الكهربائي من 1.9 إلى 3.9 فولت
CSN (Chip Select Not) وهو عبارة عن منفذ نشط منخفض. نحتاج إلى إبقائه مرتفعًا إلا عندما نرسل للجهاز أمر SPI أو عندما نتلقى بيانات على ناقل SPI من وحدة التحكم.
MOSI (Master Out Slave In) مدخل SPI إلى nRF24L01. يتم استخدامه لتلقي البيانات من متحكم
IRQ إرسال إشارة تنبيه عند توفر بيانات جديدة للمعالجة.
MISO (Master In Slave Out) إنه خرج SPI من nRF24L01. يتم استخدامه لإرسال البيانات إلى وحدة التحكم
SCK (Serial Clock) يقبل نبضات الساعة التي يوفرها مدير ناقل SPI.
CE (Chip Enable) منفذ التمكين الخاص بالوحدة المستخدمة لتحديد وضع nRF24L01 مرسل أو مستقبل، اعتمادًا على الوضع الموجود فيه حاليًا.
GND الأرضي

 

 

 

 

البرمجة

بالبداية تحتاج إلى تحميل مكتبة (RF24 master ) موجدة في إدارة المكتبات في بيئة تطوير الأردوينو،

يمكن معرفة اضافة المكتبات من خلال الرابط 

الشفرة البرمجية للمرسل 

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
RF24 radio(8,9); // CE, CSN
const byte address[6] = "00001";
char xyData[32] = "";
int joystick[2];
void setup() {
Serial.begin(9600);
radio.begin();
radio.openWritingPipe(address);
radio.setPALevel(RF24_PA_MAX);
radio.stopListening();
}
void loop() {

joystick[0] = analogRead(A4);
joystick[1] = analogRead(A3);

radio.write( joystick, sizeof(joystick) );
}

الشفرة البرمجية للمستقبل 

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#define enA 6 
#define in1 7
#define in2 5
#define enB 3 
#define in3 4
#define in4 2

RF24 radio(8,9); // CE, CSN
const byte address[6] = "00001";
char receivedData[32] = "";
int xAxis, yAxis;
int motorSpeedA = 0;
int motorSpeedB = 0;
int joystick[2];

void setup() {
pinMode(enA, OUTPUT);
pinMode(enB, OUTPUT);
pinMode(in1, OUTPUT);
pinMode(in2, OUTPUT);
pinMode(in3, OUTPUT);
pinMode(in4, OUTPUT);
Serial.begin(9600);
radio.begin();
radio.openReadingPipe(0, address);
radio.setPALevel(RF24_PA_MAX);
radio.startListening();

digitalWrite(in1, LOW);
digitalWrite(in2, LOW);
digitalWrite(in3, LOW);
digitalWrite(in4, LOW);
}
void loop() {

if (radio.available()) { // If the NRF240L01 module received data

radio.read( joystick, sizeof(joystick) );

radio.read(&receivedData, sizeof(receivedData));
yAxis = joystick[0];
xAxis = joystick[1];

Serial.println(yAxis);
Serial.println(xAxis);

}

if (yAxis < 470) {

digitalWrite(in1, HIGH);
digitalWrite(in2, LOW);

digitalWrite(in3, HIGH);
digitalWrite(in4, LOW);

motorSpeedA = map(yAxis, 470, 0, 0, 255);
motorSpeedB = map(yAxis, 470, 0, 0, 255);
}
else if (yAxis > 550) {

digitalWrite(in1, LOW);
digitalWrite(in2, HIGH);

digitalWrite(in3, LOW);
digitalWrite(in4, HIGH);

motorSpeedA = map(yAxis, 550, 1023, 0, 255);
motorSpeedB = map(yAxis, 550, 1023, 0, 255);
}

else {
motorSpeedA = 0;
motorSpeedB = 0;
}

if (xAxis < 470) {

int xMapped = map(xAxis, 470, 0, 0, 255);

motorSpeedA = motorSpeedA - xMapped;
motorSpeedB = motorSpeedB + xMapped;
// Confine the range from 0 to 255
if (motorSpeedA < 0) {
motorSpeedA = 0;
}
if (motorSpeedB > 255) {
motorSpeedB = 255;
}
}
if (xAxis > 550) {

int xMapped = map(xAxis, 550, 1023, 0, 255);

motorSpeedA = motorSpeedA + xMapped;
motorSpeedB = motorSpeedB - xMapped;

if (motorSpeedA > 255) {
motorSpeedA = 255;
}
if (motorSpeedB < 0) {
motorSpeedB = 0;
}
}

if (motorSpeedA < 70) {
motorSpeedA = 0;
}
if (motorSpeedB < 70) {
motorSpeedB = 0;
}
analogWrite(enA, motorSpeedA); // Send PWM signal to motor A
analogWrite(enB, motorSpeedB); // Send PWM signal to motor B
}

شرح الشفرة البرمجية للمرسل 

 

تضمين المكتبات الضرورية مثل (SPI,nRF24L01,RF24)

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

إنشاء كائن (RF24) يحتوي على معاملين و هي منافذ (CE, CSN)

RF24 radio(8,9); // CE, CSN

نحتاج إلى إنشاء مصفوفة تمثل العنوان الذي ستتواصل من خلاله وحدة الإرسال و الاستقبال. يمكننا تغيير قيمة العنوان إلى أي سلسلة مكونة من 5 أحرف وهذا يتيح اختيار أي جهاز استقبال سيوصل مع المرسل، سيكون نفس العنوان في كل من جهاز الاستقبال وجهاز الإرسال.

const byte address[6] = "00001";

نحدد حجم ونوع البيانات التي سيتم إرسالها

char xyData[32] = "";

نعرف مصفوفة من متغيرين ستمثل قيمة محاور عصا التحكم (x,y)

int joystick[2];

في دالة التهئية نهيئ الاتصال التسلسلي و الراديو

void setup() {
Serial.begin(9600);
radio.begin();

نحتاج إلى تهيئة كائن الراديو باستخدام وظيفة radio.openWritingPipe () ونعين عنوان جهاز الاستقبال الذي سنرسل إليه البيانات

radio.openWritingPipe(address);

ثم باستخدام وظيفة radio.setPALevel () ، نعين مستوى مضخم الطاقة ، وفي حالتنا سأقوم بتعيينه على الحد الأعلى حتى نتمكن من التحكم بالروبوت على مسافات أكبر.

radio.setPALevel(RF24_PA_MAX);

نستخدم دالة radio.stopListening(); لتحديد أن الوحدة هي وحدة الإرسال

radio.stopListening();
}

في دالة void loop نحدد مصفوفة لقيم محور y والتي تحدد حركة الماتور إلى الأمام والخلف ومصفوفة لقيم محور xالتي تحدد اتجاه الحركة إلى اليمين أو اليسار

void loop() {

joystick[0] = analogRead(A4);
joystick[1] = analogRead(A3);

نستخدم دالة radio.write لإرسال البيانات و تحتوي الدالة على معيارين الأول يمثل قيمة عصا التحكم و الثاني حجم البيانات التي سيتم نقلها و حددناها (sizeof) بمعنى العدد الفعلي من البايتات (Byts) لمصفوفة (Joystick))

radio.write( joystick, sizeof(joystick) );
}

 

شرح الشفرة البرمجية للمستقبل

 

تضمين المكتبات الضرورية مثل (SPI) و (nRF24L01) و(RF24)

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

تعريف منافذ وحدة L298

#define enA 6 
#define in1 7
#define in2 5
#define enB 3 
#define in3 4
#define in4 2

إنشاء كائن (RF24) يحتوي على معاملين و هي منافذ (CE, CSN)

RF24 radio(8,9); // CE, CSN

نحتاج إلى إنشاء مصفوفة تمثل العنوان سيكون نفس العنوان في كل من جهاز الاستقبال وجهاز الإرسال.

const byte address[6] = "00001";

نحدد حجم ونوع البيانات التي سيتم استقبالها

char receivedData[32] = "";

نعرف متغيران لقيمة (X,Y)

int xAxis, yAxis;

نحدد قيمة افتراضية تساوي 0 لسرعة المحركات motorSpeedA و motorSpeedB

int motorSpeedA = 0;
int motorSpeedB = 0;

نعرف مصفوفة من متغيرين ستمثل قيمة محاور عصا التحكم (x,y)

int joystick[2];

في دالة التهيئة نعرف منافذ الدخل و الخرج لوحدة L298

void setup() {
pinMode(enA, OUTPUT);
pinMode(enB, OUTPUT);
pinMode(in1, OUTPUT);
pinMode(in2, OUTPUT);
pinMode(in3, OUTPUT);
pinMode(in4, OUTPUT);

نهيئ الإتصال التسلسلي و الراديو

Serial.begin(9600);
radio.begin();

نحتاج إلى تهيئة كائن الراديو باستخدام وظيفة radio.openReadingPipe () ونعين عنوان جهاز الإرسال الذي سنستلم منه البيانات

radio.openReadingPipe(0, address);

ثم باستخدام وظيفة radio.setPALevel () ، نعين مستوى مضخم الطاقة ، وفي حالتنا سأقوم بتعيينه على الحد الأعلى حتى نتمكن من التحكم بالروبوت على مسافات أكبر.

radio.setPALevel(RF24_PA_MAX);

نستخدم دالة radio.startListening(); لتحديد أن الوحدة هي وحدة الاستقبال

radio.startListening();

اعطاء إشارة منخفضة لجميع منافذ التحكم في وحدة

digitalWrite(in1, LOW);
digitalWrite(in2, LOW);
digitalWrite(in3, LOW);
digitalWrite(in4, LOW);
}

نحتاج العبارة المنطقية if لنتحقق من وصول إشارة

if (radio.available()) { // If the NRF240L01 module received data

نستخدم دالة radio.read لاستقبال البيانات و تحتوي الدالة على معيارين الأول يمثل قيمة عصا التحكم و الثاني حجم البيانات التي سيتم استلامها و حددناها (sizeof) بمعنى العدد الفعلي من البايتات (Byts) لمصفوفة (Joystick)

radio.read( joystick, sizeof(joystick) );

تخزين البيانات في مصفوفة joystick

radio.read(&receivedData, sizeof(receivedData));
yAxis = joystick[0];
xAxis = joystick[1];

نستخدمة دالة Serial.println لطباعة قيم عصا التحكم (ان احتجت للتحقق من القراءات)

Serial.println(yAxis);
Serial.println(xAxis);}

يستخدم المحور Y للتحكم بالحركة للأمام أو الخلف

if (yAxis < 470) {

يتحرك الروبوت إلى الخلف

digitalWrite(in1, HIGH);
digitalWrite(in2, LOW);

digitalWrite(in3, HIGH);
digitalWrite(in4, LOW);

قم بتحويل قراءات المحور Y المتدنية للعودة للخلف من 470 إلى 0 إلى قيمة 0 إلى 255 لإشارة PWM لزيادة سرعة المحرك

motorSpeedA = map(yAxis, 470, 0, 0, 255);
motorSpeedB = map(yAxis, 470, 0, 0, 255);
}

يتحرك الروبوت إلى الأمام

else if (yAxis > 550) {

digitalWrite(in1, LOW);
digitalWrite(in2, HIGH);

digitalWrite(in3, LOW);
digitalWrite(in4, HIGH);

قم بتحويل قراءات المحور Y المتدنية للتحرك للأمام من 470 إلى 0 إلى قيمة 0 إلى 255 لإشارة PWM لزيادة سرعة المحرك

motorSpeedA = map(yAxis, 550, 1023, 0, 255);
motorSpeedB = map(yAxis, 550, 1023, 0, 255);
}

إذا بقيت عصا التحكم في المنتصف فإن المحركات لا تتحرك

else {
motorSpeedA = 0;
motorSpeedB = 0;
}

يتحكم محور X بالمحركات للتحرك باتجاه اليمين و اليسار

if (xAxis < 470) {

قم بتحويل قراءات المحور X المتزايدة من 0 إلى 470 إلى قيمة 0 إلى 255

int xMapped = map(xAxis, 470, 0, 0, 255);

يتحرك لليمين بتقليل سرعة المحرك اليمين و زيادة سرعة المحرك على اليسار

motorSpeedA = motorSpeedA - xMapped;
motorSpeedB = motorSpeedB + xMapped;
// Confine the range from 0 to 255
if (motorSpeedA < 0) {
motorSpeedA = 0;
}
if (motorSpeedB > 255) {
motorSpeedB = 255;
}
}
if (xAxis > 550) {

قم بتحويل قراءات المحور X المتزايدة من 550 إلى 1024 إلى قيمة 0 إلى 255

int xMapped = map(xAxis, 550, 1023, 0, 255);

يتحرك لليسار بتقليل سرعة المحرك اليسار و زيادة سرعة المحرك على اليمين

motorSpeedA = motorSpeedA + xMapped;
motorSpeedB = motorSpeedB - xMapped;

if (motorSpeedA > 255) {
motorSpeedA = 255;
}
if (motorSpeedB < 0) {
motorSpeedB = 0;
}
}

if (motorSpeedA < 70) {
motorSpeedA = 0;
}
if (motorSpeedB < 70) {
motorSpeedB = 0;
}

إرسال اشارة تضمين عرض النبضة

analogWrite(enA, motorSpeedA); // Send PWM signal to motor A
analogWrite(enB, motorSpeedB); // Send PWM signal to motor B
}

 




شاشة تعرض آيات قرآنية باستخدام الراسبيري

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

شاشة عرض الآيات

المواد والأدوات المطلوبة

 

راسبيري باي

1X راسبيري باي

HDMI wire

1X سلك (HDIM)

محول طاقة راسبيري باي

1Xمحول طاقة

ذاكرة

1Xذاكرة

قارئ بطاقة الذاكرة

1Xقارئ ذاكرة

المرآة الذكية

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

قم بتنزيل و تثبيت أحدث إصدار من (Node.js) :

curl -sL https://deb.nodesource.com/setup_16.x | sudo -E bash -
sudo apt install -y nodejs

نسخ مجلدات المرآة الذكية

git clone https://github.com/MichMich/MagicMirror

سيتم نسخ المجلدات داخل مجلد باسم (MagicMirror) قم بفتح المجلد من خلال السطر

cd MagicMirror/

قم بتثبيت التطبيق من خلال السطر التالي

npm install --only=prod --omit=dev

قم بعمل نسخة من ملف نموذج التكوين من خلال السطر

cp config/config.js.sample config/config.js

الآن اصبح المرآة الذكية جاهزة يمكن تشغيلها و ستعرض لك بعض البيانات المثبته عليها بشكل تلقائي ، تشغيلها من خلال السطر التالي

npm run start

للخروج من واجهة المرآة قم بالنقر على (Ctrl+M)

يمكن التعديل و حذف البيانات التي تظهر على واجهة المرآة من خلال ملف التكون (config) و الموجود فيالمسار (~MagicMirror\config)

قم بفتح الملف من خلال محرر( Geany )

let config = {
address: "localhost", // Address to listen on, can be:
// - "localhost", "127.0.0.1", "::1" to listen on loopback interface
// - another specific IPv4/6 to listen on a specific interface
// - "0.0.0.0", "::" to listen on any interface
// Default, when address config is left out or empty, is "localhost"
port: 8080,
basePath: "/", // The URL path where MagicMirror is hosted. If you are using a Reverse proxy
// you must set the sub path here. basePath must end with a /
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"], // Set [] to allow all IP addresses
// or add a specific IPv4 of 192.168.1.5 :
// ["127.0.0.1", "::ffff:127.0.0.1", "::1", "::ffff:192.168.1.5"],
// or IPv4 range of 192.168.3.0 --> 192.168.3.15 use CIDR format :
// ["127.0.0.1", "::ffff:127.0.0.1", "::1", "::ffff:192.168.3.0/28"],

useHttps: false, // Support HTTPS or not, default "false" will use HTTP
httpsPrivateKey: "", // HTTPS private key path, only require when useHttps is true
httpsCertificate: "", // HTTPS Certificate path, only require when useHttps is true

language: "en",
locale: "en-US",
logLevel: ["INFO", "LOG", "WARN", "ERROR"], // Add "DEBUG" for even more logging
timeFormat: 24,
units: "metric",
// serverOnly: true/false/"local" ,
// local for armv6l processors, default
// starts serveronly and then starts chrome browser
// false, default for all NON-armv6l devices
// true, force serveronly mode, because you want to.. no UI on this device

modules: [
{
module: "alert",
},
{
module: "updatenotification",
position: "top_bar"
},
{
module: "clock",
position: "top_left"
},
{
module: "calendar",
header: "US Holidays",
position: "top_left",
config: {
calendars: [
{
symbol: "calendar-check",
url: "webcal://www.calendarlabs.com/ical-calendar/ics/76/US_Holidays.ics"
}
]
}
},
{
module: "compliments",
position: "lower_third"
},
{
module: "weather",
position: "top_right",
config: {
weatherProvider: "openweathermap",
type: "current",
location: "New York",
locationID: "5128581", //ID from http://bulk.openweathermap.org/sample/city.list.json.gz; unzip the gz file and find your city
apiKey: "YOUR_OPENWEATHER_API_KEY"
}
},
{
module: "weather",
position: "top_right",
header: "Weather Forecast",
config: {
weatherProvider: "openweathermap",
type: "forecast",
location: "New York",
locationID: "5128581", //ID from http://bulk.openweathermap.org/sample/city.list.json.gz; unzip the gz file and find your city
apiKey: "YOUR_OPENWEATHER_API_KEY"
}
},
{
module: "newsfeed",
position: "bottom_bar",
config: {
feeds: [
{
title: "New York Times",
url: "https://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml"
}
],
showSourceTitle: true,
showPublishDate: true,
broadcastNewsFeeds: true,
broadcastNewsUpdates: true
}
},
]
};

/*************** DO NOT EDIT THE LINE BELOW ***************/
if (typeof module !== "undefined") {module.exports = config;}

شرح ملف التكوين

في البداية يحدد الملف البيانات الأساسية التي تحدد عنوان الجهاز و اللغة

في برنامجنا فمنا بتغير اللغة إلى اللغة العربية بكتابة قيمة “ar” بدلا من “en”

و الموقع السعودية بكتابة “locale: “ar-sa”,” بدلا من  “en-US”

بالاضافة إلى تغير عرض الوقت إلى نظام 12 ساعة بدلا من 24

let config = {
address: "localhost", // Address to listen on, can be:
// - "localhost", "127.0.0.1", "::1" to listen on loopback interface
// - another specific IPv4/6 to listen on a specific interface
// - "0.0.0.0", "::" to listen on any interface
// Default, when address config is left out or empty, is "localhost"
port: 8080,
basePath: "/", // The URL path where MagicMirror is hosted. If you are using a Reverse proxy
// you must set the sub path here. basePath must end with a /
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"], // Set [] to allow all IP addresses
// or add a specific IPv4 of 192.168.1.5 :
// ["127.0.0.1", "::ffff:127.0.0.1", "::1", "::ffff:192.168.1.5"],
// or IPv4 range of 192.168.3.0 --> 192.168.3.15 use CIDR format :
// ["127.0.0.1", "::ffff:127.0.0.1", "::1", "::ffff:192.168.3.0/28"],

useHttps: false, // Support HTTPS or not, default "false" will use HTTP
httpsPrivateKey: "", // HTTPS private key path, only require when useHttps is true
httpsCertificate: "", // HTTPS Certificate path, only require when useHttps is true

language: "en",
locale: "en-US",
logLevel: ["INFO", "LOG", "WARN", "ERROR"], // Add "DEBUG" for even more logging
timeFormat: 24,
units: "metric",

من خلال “modules” نحدد البيانات التي نود اضهارها في هذه المشروع لا احتاج إلى أي من هذه البيانات معدا الوقت والتاريخ الذي يعرضه داله “clock”

بعد حذف أو عمل تعليق على باقي البيانات ، قم بتشغيل المرآة للتأكد أنها تعمل بشكل صحيح بدون اظهار أي بيانات لا نتطلبها

modules: [
{
module: "alert",
},
{
module: "updatenotification",
position: "top_bar"
},
{
module: "clock",
position: "top_left"
},
{
module: "calendar",
header: "US Holidays",
position: "top_left",
config: {
calendars: [
{
symbol: "calendar-check",
url: "webcal://www.calendarlabs.com/ical-calendar/ics/76/US_Holidays.ics"
}
]
}
},
{
module: "compliments",
position: "lower_third"
},
{
module: "weather",
position: "top_right",
config: {
weatherProvider: "openweathermap",
type: "current",
location: "New York",
locationID: "5128581", //ID from http://bulk.openweathermap.org/sample/city.list.json.gz; unzip the gz file and find your city
apiKey: "YOUR_OPENWEATHER_API_KEY"
}
},
{
module: "weather",
position: "top_right",
header: "Weather Forecast",
config: {
weatherProvider: "openweathermap",
type: "forecast",
location: "New York",
locationID: "5128581", //ID from http://bulk.openweathermap.org/sample/city.list.json.gz; unzip the gz file and find your city
apiKey: "YOUR_OPENWEATHER_API_KEY"
}
},
{
module: "newsfeed",
position: "bottom_bar",
config: {
feeds: [
{
title: "New York Times",
url: "https://rss.nytimes.com/services/xml/rss/nyt/HomePage.xml"
}
],
showSourceTitle: true,
showPublishDate: true,
broadcastNewsFeeds: true,
broadcastNewsUpdates: true
}
},
]
};

/*************** DO NOT EDIT THE LINE BELOW ***************/
if (typeof module !== "undefined") {module.exports = config;}

سيكون برنامج التكوين بهذا الشكل

/* Magic Mirror Config Sample
*
* By Michael Teeuw https://michaelteeuw.nl
* MIT Licensed.
*
* For more information on how you can configure this file
* see https://docs.magicmirror.builders/getting-started/configuration.html#general
* and https://docs.magicmirror.builders/modules/configuration.html
*/
let config = {
address: "localhost", // Address to listen on, can be:
// - "localhost", "127.0.0.1", "::1" to listen on loopback interface
// - another specific IPv4/6 to listen on a specific interface
// - "0.0.0.0", "::" to listen on any interface
// Default, when address config is left out or empty, is "localhost"
port: 8080,
basePath: "/", // The URL path where MagicMirror is hosted. If you are using a Reverse proxy
// you must set the sub path here. basePath must end with a /
ipWhitelist: ["127.0.0.1", "::ffff:127.0.0.1", "::1"], // Set [] to allow all IP addresses
// or add a specific IPv4 of 192.168.1.5 :
// ["127.0.0.1", "::ffff:127.0.0.1", "::1", "::ffff:192.168.1.5"],
// or IPv4 range of 192.168.3.0 --> 192.168.3.15 use CIDR format :
// ["127.0.0.1", "::ffff:127.0.0.1", "::1", "::ffff:192.168.3.0/28"],

useHttps: false, // Support HTTPS or not, default "false" will use HTTP
httpsPrivateKey: "", // HTTPS private key path, only require when useHttps is true
httpsCertificate: "", // HTTPS Certificate path, only require when useHttps is true

language: "ar",
locale: "ar-SA",
logLevel: ["INFO", "LOG", "WARN", "ERROR"], // Add "DEBUG" for even more logging
timeFormat: 12,
units: "metric",
// serverOnly: true/false/"local" ,
// local for armv6l processors, default
// starts serveronly and then starts chrome browser
// false, default for all NON-armv6l devices
// true, force serveronly mode, because you want to.. no UI on this device

modules: [

{
module: "clock",
position: "top_left"
},

]
};

/*************** DO NOT EDIT THE LINE BELOW ***************/
if (typeof module !== "undefined") {module.exports = config;}

و سيظهر البرنامج بعد التشغيل بهذا الشكل

شاشة عرض الوقت فقط

عرض التاريخ بالتقويم الهجري

الان سنقوم بعرض التاريخ الهجري

أولا سنقوم بكتاية السطر التالي لنسخ برنامج كتابة التاريخ مباشرة ولكن يجب أن يتم تحميله داخل ملف (modules)

سيتم تحميل ملف باسم (islamic-dash-display) و يوجد بداخلة ملف (currentislamicdate) قم بنقل ملف إلى modules

cd modules
git clone https://github.com/GeeksValley/islamic-dash-display

ثم سنقوم باضافة الوحدة إلى ملف التكوين من خلال الأسطر التالية

{
    module: 'currentislamicdate',
    position: 'top_right', // This can be any of the regions, best results in center regions
},

ستكون النتيجة بهذا الشكل

تشغيل البرنامج بنفس الطريقة

cd MagicMirror
npm run start

شاشة عرض الوقت فقط

السطر الأول يعطي اسم الوحدة و السطر الثاني يحدد الموقع الذي سيتم ادراج الوحدة عليه

من خلال الصورة التالية تستطيع تحديد الموقع حسب ما تريد

تحديد مواقع عرض الوحدة بشاشة العرض

عرض آيات قرآنية

نتبع الخطوات التالية لإضافة وحدة عرض الآيات القرآنية

أولا ننتقل إلى ملف (module) الموجود داخل ملف (MagicMirror)

cd Modules

ثم نقوم بعمل نسخ من ملف وحدة عرض الآيات من خلال السطر التالي

git clone https://github.com/slametps/MMM-RandomQuranAyah.git

بعد ذلك اكتب الأسطر التالية

cd MMM-RandomQuranAyah
npm install async

ثم للخروج من الملف

cd --

عد إلى ملف التكوين واضف السطر التالية

{
module: 'MMM-RandomQuranAyah',
position: 'lower_third', // This can be any of the regions. Best result is in the top_bar/bottom_bar as ayah (verse) can take multiple lines.
config: {
    apiVersion: '1.0', // please, leave unchanged. reserved for future use.
    showArabic: true,
    showTranslation: false,
    surahArabicName:true,
    translationLang:'id.indonesian',
    updateInterval: 70* 100, // milliseconds
}
},

يحدد السطر updateInterval المدة لعرض كل آيه قبل الانتقال لعرض آيه جديدة

 

أخيرا عد إلى تشغيل البرنامج بنفس الطريقة

cd MagicMirror
npm run start

ستظهر لك الآيات على الشاشة

شاشة عرض الآيات

للتحكم بحجم خط الآيات فقد يبدو صغيرا اذا كانت شاشة العرض بمقاس أكبر من 10 انش

  (MMM-RandomQuranAyah.css)انتقل إلى ملف عدل الأسطر كالتالي

.MMM-RandomQuranAyah .txt-arabic {
color: white;
font-size: 65px;

}
.MMM-RandomQuranAyah .txt-translation {
color: white;
}

 

عرض أوقات الصلاة

يمكن عرض أوقات الصلاة من خلال اضافة وحدة أوقات الصلاة داخل ملف (Modules) بالخطوات التالية

cd modules

ثم نقوم بعمل نسخ من ملف وحدة عرض أوقات الصلاة من خلال السطر التالي

git clone https://github.com/slametps/MMM-PrayerTime.git

بعد ذلك اكتب الأسطر التالية

cd MMM-PrayerTime
npm install async

ثم الخروج من الملف بكتابة

cd --

هذه الخطوة اختيارية لتعديل ظهور الجدول من اليمين إلى اليسار وهو بحذف ملف التكوين(MMM-PrayerTime.js) من داخل ملف (MMM-PrayerTime) و استبداله بالبرنامج التالي

Module.register("MMM-PrayerTime",{
// Default module config.
defaults: {
apiVersion: '1.0',
lat: false,
lon: false,
timezone: false,
timeFormat: config.timeFormat || 24,
method: 5, // method of timing computation {0-Shia Ithna-Ashari,1-University of Islamic Sciences, Karachi,2-Islamic Society of North America (ISNA),3-Muslim World League (MWL),4-Umm al-Qura, Makkah,5-Egyptian General Authority of Survey,7-Institute of Geophysics, University of Tehran}
methodSettings: false,
school: 0, // 0 = Shafii, 1 = Hanafi
adjustment: 0, // 0 = no days of adjustment to hijri date(s)
tune: '', // Comma Separated String of integers to offset timings returned by the API in minutes. Example: 5,3,5,7,9,7. See https://aladhan.com/calculation-methods
midnightMode: 0, // 0 for Standard (Mid Sunset to Sunrise), 1 for Jafari (Mid Sunset to Fajr). If you leave this empty, it defaults to Standard.
latitudeAdjustmentMethod: '', // Method for adjusting times higher latitudes - for instance, if you are checking timings in the UK or Sweden. 1 - Middle of the Night, 2 - One Seventh, 3 - Angle Based
playAdzan: ['fajr', 'dhuhr', 'asr', 'maghrib', 'isha'],
notDisplayed: ['midnight', 'sunset'],
useUpdateInterval: true,
updateInterval: 86400 * 1000, // How often do you want to fetch new praying time? (milliseconds)
animationSpeed: 2.5 * 1000, // Speed of the update animation. (milliseconds)
language: config.language || "en",
colored: false,
showAdzanAlert: true,
showTomorrow: true,
vertical: true, // set false to horizontal view
alertTimer: 15000
},

getScripts: function() {
return ["moment.js"];
},

getStyles: function() {
return ["MMM-PrayerTime.css"];
},

// Define required translations.
getTranslations: function() {
return {
'en': 'translations/en.json',
'id': 'translations/id.json',
'ar': 'translations/ar.json',
'fr': 'translations/fr.json',
'de': 'translations/de.json',
'bn': 'translations/bn.json'
};
},

getCommands: function(commander) {
commander.add({
command: 'prayertime',
description: this.translate("TXT_PRAYERTIME_DESC"),
callback: 'cmd_prayertime'
})
},

cmd_prayertime: function(command, handler) {
var text = "";
text += "*" + this.translate("TXT_PRAYERTIME") + "*\n";
text += "*" + this.translate("TODAY") + "*\n";
for (var t in this.arrTodaySchedule) {
text += "*" + this.translate(this.arrTodaySchedule[t][0].toUpperCase()) + ":* `" + (this.config.timeFormat == 12 ? moment(this.arrTodaySchedule[t][1], ["HH:mm"]).format("h:mm A") : this.arrTodaySchedule[t][1]) + "`\n";
}
text += "\n*" + this.translate("TOMORROW") + "*\n";
for (var t in this.arrNextdaySchedule) {
text += "*" + this.translate(this.arrNextdaySchedule[t][0].toUpperCase()) + ":* `" + (this.config.timeFormat == 12 ? moment(this.arrNextdaySchedule[t][1], ["HH:mm"]).format("h:mm A") : this.arrNextdaySchedule[t][1]) + "`\n";
}
handler.reply("TEXT", text, {parse_mode:'Markdown'});
},


/* getParams
* Generates an url with api parameters based on the config.
*
* return String - URL params.
*/
getParams: function(unixTime) {
var params = unixTime + "?";
if(this.config.lat) {
params += "latitude=" + this.config.lat;
}
if (this.config.lon) {
params += "&longitude=" + this.config.lon;
}
if (this.config.timezone) {
params += "&timezonestring=" + this.config.timezone;
}
if (this.config.method) {
params += "&method=" + this.config.method;
}
if (this.config.methodSettings) {
params += "&methodSettings=" + encodeURI(this.config.methodSettings);
}
if (this.config.school) {
params += "&school=" + this.config.school;
}
if (this.config.adjustment) {
params += "&adjustment=" + this.config.adjustment;
}
if (this.config.tune) {
params += "&tune=" + encodeURI(this.config.tune);
}
if (this.config.midnightMode) {
params += "&midnightMode=" + this.config.midnightMode;
}
if (this.config.latitudeAdjustmentMethod) {
params += "&latitudeAdjustmentMethod=" + this.config.latitudeAdjustmentMethod;
}

return params;
},

/* processSchedule
* process downloaded scheduled.
*/
processSchedule: function() {
var self = this;

function sortSchedule(a, b) {
if (a[1] < b[1]) {
return -1;
}
if (a[1] > b[1]) {
return 1;
}

// names must be equal
return 0;
}

// sort today schedule
this.arrTodaySchedule = [];
this.arrAdzanTime = [];
for(var x in this.todaySchedule){
if (!self.config.notDisplayed.includes(x.toLowerCase()))
this.arrTodaySchedule.push([x, this.todaySchedule[x]]);
if (self.config.playAdzan.includes(x.toLowerCase()))
this.arrAdzanTime.push(this.todaySchedule[x]);
}
this.arrTodaySchedule.sort(sortSchedule);

// sort nextday schedule
this.arrNextdaySchedule = [];
for(var x in this.nextdaySchedule){
if (!self.config.notDisplayed.includes(x.toLowerCase()))
this.arrNextdaySchedule.push([x, this.nextdaySchedule[x]]);
}
this.arrNextdaySchedule.sort(sortSchedule);

this.loaded = true;
this.updateDom(this.config.animationSpeed);
},

updateSchedule: function(delay) {
var self = this;
Log.log(self.name + ': updateSchedule');
var urlBase = "http://api.aladhan.com/timings/";
var curUnixTime = moment().unix();
var urlToday = urlBase + this.getParams(curUnixTime);
var urlNextday = urlBase + this.getParams(curUnixTime + 86400);
var resultToday = {};
var resultNextday = {};
var nbReq = 2;
var nbRes = 0;

var todayRequest = new XMLHttpRequest();
todayRequest.open("GET", urlToday, true);
todayRequest.onreadystatechange = function() {
if (this.readyState === 4) {
if (this.status === 200) {
resultToday = JSON.parse(this.responseText);
self.todaySchedule = resultToday.data.timings;
// debug/testing only
//self.todaySchedule = {"Fajr":"04:30", "Dhuhr":"12:00", "Asr":"16:14", "Maghrib":"18:00", "Isha":"20:50", "Imsak":"04:20"};
nbRes++;
if (nbRes == nbReq)
self.processSchedule();
} else {
Log.error(self.name + ": got HTTP status-" + this.status);
retry = true;
}
}
};
todayRequest.send();

var nextdayRequest = new XMLHttpRequest();
nextdayRequest.open("GET", urlNextday, true);
nextdayRequest.onreadystatechange = function() {
if (this.readyState === 4) {
if (this.status === 200) {
resultNextday = JSON.parse(this.responseText);
self.nextdaySchedule = resultNextday.data.timings;
nbRes++;
if (nbRes == nbReq)
self.processSchedule();
} else {
Log.error(self.name + ": got HTTP status-" + this.status);
retry = true;
}
}
};
nextdayRequest.send();
},

isAdzanNow: function() {
var curTime = moment().format("HH:mm:ss");
var indexAdzan = -1;
//console.log(this.arrTodaySchedule);
if (this.arrTodaySchedule.length > 0)
{
function isAdzan(el, idx, arr) {
return (el[1] + ':00') == curTime;
}
indexAdzan = this.arrTodaySchedule.findIndex(isAdzan);
//console.log("indexAdzan-"+indexAdzan);

if (indexAdzan > -1) {
//console.log(this.config.playAdzan);
//console.log("this.arrTodaySchedule[indexAdzan][0]).toLowerCase()-"+(this.arrTodaySchedule[indexAdzan][0]).toLowerCase());
//console.log("this.config.playAdzan.findIndex((this.arrTodaySchedule[indexAdzan][0]).toLowerCase())-"+this.config.playAdzan.findIndex((this.arrTodaySchedule[indexAdzan][0]).toLowerCase()));
if (this.config.playAdzan.includes((this.arrTodaySchedule[indexAdzan][0]).toLowerCase())) {
if (this.config.showAdzanAlert) {
var occasionNameUpper = (this.arrTodaySchedule[indexAdzan][0]).toUpperCase();
var alertMsg = "ALERT_ADZAN_MSG";
var adzanImsak = "ADZAN";
//console.log("occasionNameUpper-"+occasionNameUpper);
if (occasionNameUpper == "IMSAK") {
alertMsg = "ALERT_IMSAK_MSG";
adzanImsak = "IMSAK";
}
this.sendNotification("SHOW_ALERT", {title: this.translate(adzanImsak).toUpperCase(), imageFA: 'bullhorn', message: this.translate(alertMsg).replace("%OCCASION", this.translate(occasionNameUpper)), timer: this.config.alertTimer});
}
//console.log("this.arrTodaySchedule[indexAdzan][0]).toUpperCase()-"+(this.arrTodaySchedule[indexAdzan][0]).toUpperCase());
this.sendSocketNotification("PLAY_ADZAN", {occasion: (this.arrTodaySchedule[indexAdzan][0]).toUpperCase()});
}
}
}
},

start: function() {
Log.info("Starting module: " + this.name);
var self = this;

// Set locale.
moment.locale(this.config.language);

this.todaySchedule = {};
this.nextdaySchedule = {};
this.arrTodaySchedule = [];
this.arrNextdaySchedule = [];
this.arrAdzanTime = [];

this.loaded = false;
var self = this;

// first update
self.updateSchedule(0);
// periodic update if defined
if (self.config.useUpdateInterval) {
Log.log(self.name + ': using periodic update is activated');
setInterval(function() {
self.updateSchedule(0);
}, self.config.updateInterval);
}
// adzan-checker
self.isAdzanNow();
setInterval(function() {
self.isAdzanNow();
}, 1000);
},

// Override dom generator.
getDom: function() {
Log.log("Updating MMM-PrayerTime DOM.");
var self = this;
var wrapper = document.createElement("div");

if (!this.loaded) {
wrapper.innerHTML = this.translate("LOADING");
wrapper.className = "dimmed light small";
}
else {
var table = document.createElement("table");
table.className = "small";

if (this.config.vertical) { // vertical view
var row = document.createElement("tr");
if (this.config.colored) {
row.className = "colored";
}
table.appendChild(row);

if (this.config.showTomorrow) {
// nextday
var occasionTimeNext = document.createElement("td");
occasionTimeNext.className = "occasion-time bright light";
//occasionTimeNext.innerHTML = this.todaySchedule[t];
occasionTimeNext.innerHTML = this.translate('TOMORROW');
row.appendChild(occasionTimeNext);
}




// today
var occasionTime = document.createElement("td");
occasionTime.className = "occasion-time bright light";
occasionTime.innerHTML = this.translate('TODAY');
row.appendChild(occasionTime);

var occasionName = document.createElement("td");
occasionName.className = "occasion-name bright light";
occasionName.innerHTML = '&nbsp;';
row.appendChild(occasionName);




//for (var i = 0, count = this.todaySchedule.length; i < count; i++) {
//for (t in this.todaySchedule)
for (t in this.arrTodaySchedule)
{
row = document.createElement("tr");
if (this.config.colored) {
row.className = "colored";
}
table.appendChild(row);
//// ثاني تغير 
if (this.config.showTomorrow) {
// nextday
var occasionTimeNext = document.createElement("td");
occasionTimeNext.className = "occasion-time bright light";
//occasionTimeNext.innerHTML = this.todaySchedule[t];
occasionTimeNext.innerHTML = (this.config.timeFormat == 12 ? moment(this.arrNextdaySchedule[t][1], ["HH:mm"]).format("h:mm A") : this.arrNextdaySchedule[t][1]);
row.appendChild(occasionTimeNext);
}




// today
var occasionTime = document.createElement("td");
occasionTime.className = "occasion-time bright light";
//occasionTime.innerHTML = this.todaySchedule[t];
occasionTime.innerHTML = (this.config.timeFormat == 12 ? moment(this.arrTodaySchedule[t][1], ["HH:mm"]).format("h:mm A") : this.arrTodaySchedule[t][1]);
row.appendChild(occasionTime);

var occasionName = document.createElement("td");
occasionName.className = "occasion-name bright light";
//occasionName.innerHTML = this.translate(t);
occasionName.innerHTML = this.translate(this.arrTodaySchedule[t][0].toUpperCase());
row.appendChild(occasionName);


}
}
else { // horizontal view
var table = document.createElement("table");
table.className = "small";

var row = document.createElement("tr");
if (this.config.colored) {
row.className = "colored";
}
table.appendChild(row);

var occasionName = document.createElement("td");
occasionName.className = "occasion-name bright light";
occasionName.innerHTML = '&nbsp;';
row.appendChild(occasionName);

// column label
for (t in this.arrTodaySchedule) {
var occasionTime = document.createElement("td");
occasionTime.className = "occasion-time bright light";
occasionTime.innerHTML = this.translate(this.arrTodaySchedule[t][0].toUpperCase());
row.appendChild(occasionTime);
}

// today
var rowToday = document.createElement("tr");
if (this.config.colored) {
rowToday.className = "colored";
}
table.appendChild(rowToday);

var occasionNameToday = document.createElement("td");
occasionNameToday.className = "occasion-time bright light";
occasionNameToday.innerHTML = this.translate('TODAY');
rowToday.appendChild(occasionNameToday);
for (t in this.arrTodaySchedule) {
var occasionTimeToday = document.createElement("td");
occasionTimeToday.className = "occasion-time bright light";
occasionTimeToday.innerHTML = (this.config.timeFormat == 12 ? moment(this.arrTodaySchedule[t][1], ["HH:mm"]).format("h:mm A") : this.arrTodaySchedule[t][1]);
rowToday.appendChild(occasionTimeToday);
}

if (this.config.showTomorrow) {
// nextday
var rowNext = document.createElement("tr");
if (this.config.colored) {
rowNext.className = "colored";
}
table.appendChild(rowNext);

var occasionNameNext = document.createElement("td");
occasionNameNext.className = "occasion-time bright light";
occasionNameNext.innerHTML = this.translate('TOMORROW');
rowNext.appendChild(occasionNameNext);
for (t in this.arrTodaySchedule) {
var occasionTimeNext = document.createElement("td");
occasionTimeNext.className = "occasion-time bright light";
occasionTimeNext.innerHTML = (this.config.timeFormat == 12 ? moment(this.arrNextdaySchedule[t][1], ["HH:mm"]).format("h:mm A") : this.arrNextdaySchedule[t][1]);
rowNext.appendChild(occasionTimeNext);
}
}
}
wrapper.appendChild(table);
}

return wrapper;
},

notificationReceived: function(notification, payload, sender) {
Log.log(this.name + ": received notification : " + notification);
if (notification == "PRAYER_TIME") {
if (payload.type == "PLAY_ADZAN") {
if (this.config.showAdzanAlert)
this.sendNotification("SHOW_ALERT", {title: this.translate("ADZAN"), message: this.translate("ALERT_ADZAN_MSG").replace("%OCCASION", this.translate("ASR")), timer: this.config.alertTimer});
this.sendSocketNotification("PLAY_ADZAN", {occasion: 'ASR'});
}
if (payload.type == "UPDATE_PRAYINGTIME") {
this.updateSchedule(0);
}
}
}
});

 

عد إلى ملف التكوين واضف السطر التالية

 {
module: 'MMM-PrayerTime',
position: 'top_right', // This can be any of the regions. Best result is in the top_left/top_right.
config: {
apiVersion: '1.0', // please, leave unchanged. reserved for future use.
lat: 24.7136, // latitude of your position (city)
lon: 46.6753, // longitude of your position (city)
timezone: 'Asia/Riyadh', // please refer to http://php.net/manual/en/timezones.php
timeFormat: 12,
method: 5,
playAdzan: ['fajr', 'dhuhr', 'asr', 'maghrib', 'isha'],
notDisplayed: ['midnight', 'sunset'],
useUpdateInterval: true,
updateInterval: 86400 * 1000, // How often do you want to fetch new praying time? (milliseconds)
animationSpeed: 2.5 * 1000, // Speed of the update animation. (milliseconds)
language: 'ar',
showAdzanAlert: true,
showTomorrow: true,
vertical: true, // set false for horizontal view
alertTimer: 15000
}
},

يجب أن تقوم بتغير الأسطر التالية حسب موقعك فيشير (lat)  إلى خطوط العرض الخاصة بمدينتك و (lon) إلى خطوط الطول

lat: 24.7136, // latitude of your position (city)
lon: 46.6753, // longitude of your position (city)
timezone: 'Asia/Riyadh', // please refer to http://php.net/manual/en/timezones.php

الأسطر التالية تتحكم بتفعيل الأذان عند دخول أوقات الصلاة، و هل ظهور مواعيد الصلاة باليوم التالي

showAdzanAlert: true,
showTomorrow: true,

بعد تشغيل البرنامج ستجد الواجهة بهذا الشكل

عرض أوقات الصلاة

 

إضافة صور للخلفية

يمكن عرضصور مختلفة  من خلال إضافة وحدة تقوم بنشر صور من  موجودة داخل ملف  (Modules) بالخطوات التالية

cd modules

ثم نقوم بعمل نسخ من ملف وحدة عرض أوقات الصلاة من خلال السطر التالي

git clone https://github.com/miccl/MMM-Random-local-image.git

بعد ذلك اكتب الأسطر التالية

cd MMM-Random-local-image
npm install async

 الصور التي ستقوم بعرضها يجب حفظها داخل ملف MMM-Random-local-image\exampleImages احذف الصورة الموجودة بالفعل

يمكن أن تستخدم ذات الصور التي استخدمناها و تحملها من خلال الرابط أو تقوم باختيار الصور الخاصة بك مع مراحة أن يكون حجم الصورة (1920*1080)

عد إلى ملف التكوين واضف السطر التالية

 {
module: "MMM-Random-local-image",
position: "fullscreen_below",
config: {
photoDir: "./modules/MMM-Random-local-image/exampleImages",

// below are more options
}
},

وبشكل اختياري يمكن عرض الراسبيري باي بشكل أفقي من خلال

النقر على علامة التوت و من ثم اختيار PERFOMENS>SCREENCONGIGURATION

تغير عرض الراسبيري باي

ثم ننقر على VIEW > APPLAY

شاشة عرض الآيات شاشة عرض الآيات

 

 

 

 

 




التحكم بالحاسوب باستخدام الاردوينو والهاتف الذكي

مقدمة

تستطيع أن تستخدم هاتفك الذكي كفأرة تمكنك من التحكم بالحاسوب وذلك باستخدام الأردوينو ووحدة البلوتوث، سنعمل ذلك عبر برمجة الاردوينو وتوصيله بالحاسوب ليتلقى أوامر التحكم من خلال الهاتف الذكي

المواد والأدوات

smartphone-controlled-mouse

1× اردوينو اونو

smartphone-controlled-mouse

1× سلك الاردوينو

التحكم بالحاسوب عن بعد

موديول بلوتوث من النوع HC-06

التحكم بالحاسوب

حزمة أسلاك توصيل (ذكر – أنثى)

التحكم بالحاسوب

1× هاتف بنظام اندرويد

توصيل الدائرة

للمزيد حول وحدة البلوتوث يمكنك الرجوع للدرس التالي نظام التحكم في الإضاءة عبر البلوتوث.

التحكم بالحاسوب

كود اردوينو IDE

قبل رفع الكود البرمجي للوحة الاردوينو عد للدائرة الكهربائية وافصل السلكين المربوطين بالمنافذ A4 و A5.

بعد ذلك ارفع الكود البرمجي للوحة الاردوينو وبعد اكتمال عملية الرفع أعد الأسلاك كما كانت.

int datareceived[5] {0,0,0,0};          // To store byte from phone
int in_byte = 0;
int array_index = 0;
int l_prev=0,r_prev=0; // previous status of mouse left and right click 
void setup() {
Serial.begin (9600); // starts the serial monitor
}
 int height=0,width=0;
void loop() {
  int clicks=0;
  int sensitivity=20;       // you can adjust the sensitivity
  int xpos=0,ypos=0;
if (Serial.available() > 0) {  //recieve byte from phone
  in_byte= Serial.read(); //store in byte into a variable
  if (in_byte == (255)) { // if the variable is 0 stet the array inxed to 0.
    array_index = 0;
  }
  datareceived[array_index] = in_byte;  //store number into array
  array_index = array_index +1;
  
if(datareceived[1]>=110)
xpos=map(datareceived[1],110,172,0,sensitivity);       // When moved right
if(datareceived[1]<=70)
xpos=map(datareceived[1],60,1,0,-sensitivity);        // When moved left
if(datareceived[2]>=110)
ypos=map(datareceived[2],110,255,0,sensitivity);     // When moved down
if(datareceived[2]<=60)
ypos=map(datareceived[2],70,1,0,-sensitivity);       // When moved up


if(datareceived[3]==1 && l_prev==0)      // TO recognise a single button press
  clicks=1;
else if(datareceived[3]==2 && r_prev==0)
clicks=2;
else if(datareceived[3]==3 || datareceived[3]==4)
clicks=datareceived[3];  //  scroll

l_prev=datareceived[3];
r_prev=datareceived[3];

if(xpos!=0 or ypos!=0 or clicks!=0)       // when either of the joystick is moved or the button is pressed or scrolled
{
height=height+ypos;
width=width+xpos;
if(height>=799)
height=799;
if(height<=0)
height=0;
if(width>=1279)
width=1279;
if(width<=0)
width=0;
Serial.print(width);
Serial.print(":");
Serial.print(height);
Serial.print(":");
Serial.println(clicks);
clicks=0;
}
}
}

شرح الكود البرمجي

في المصفوفة datareceived ستخزن البيانات التي ستتم قراءتها من الهاتف الذكي وستكون قيمتها الابتدائية=0.

int datareceived[5] {0,0,0,0};          // To store byte from phone

في المتغيرين l_prev و r_prev=0 سيتم تخزين القيم الابتدائية لحالة الحركة ليمين ويسار الحاسوب =0.

int l_prev=0,r_prev=0; // previous status of mouse left and right click 

في دالة ()void loop:

المتغير sensitivity=20 يمثل حساسية الحركة والانتقال من مكان لآخر يمكنك تغيير القيمة.

 متغير xpos يشير إلى موقع السهم على محور x في شاشة الكمبيوتر وقيمته الابتدائية = 0.

متغير ypos يشير إلى موقع السهم على محور y في شاشة الكمبيوتر وقيمته الابتدائية = 0.

 int sensitivity=20;       // you can adjust the sensitivity
 int xpos=0,ypos=0;

في المصفوفة datareceived سيتم تخزين القيم المقروءة من الهاتف الذكي وتحديثها بشكل مستمر.

  datareceived[array_index] = in_byte;  //store number into array

بعد قراءة البيانات من الهاتف الذكي سيتم تحديد وجهة الحركة للسهم.

إذا كانت القيم المقروءة >=110 ستكون حركة السهم لليمين.

إذا كانت القيم المقروءة <=70 ستكون حركة السهم لليسار.

إذا كانت القيم المقروءة >=110 ستكون حركة السهم للأسفل. 

إذا كانت القيم المقروءة <=60 ستكون حركة السهم للأعلى.

if(datareceived[1]>=110)
xpos=map(datareceived[1],110,172,0,sensitivity);       // When moved right
if(datareceived[1]<=70)
xpos=map(datareceived[1],60,1,0,-sensitivity);        // When moved left
if(datareceived[2]>=110)
ypos=map(datareceived[2],110,255,0,sensitivity);     // When moved down
if(datareceived[2]<=60)
ypos=map(datareceived[2],70,1,0,-sensitivity);       // When moved up

هنا سيتم تحديد النقر هل هو لزر اليسار أو لزر اليمين.

if(datareceived[3]==1 && l_prev==0)      // TO recognise a single button press
  clicks=1;
else if(datareceived[3]==2 && r_prev==0)
clicks=2;
else if(datareceived[3]==3 || datareceived[3]==4)

هنا سيتم تحديد مكان الانتقال إما لأعلى أو أسفل الصفحة.

clicks=datareceived[3];  //  scroll

برنامج MIT App Inventor

حمّل تصميم واجهة المستخدم والكود البرمجي من هنا.

من المتصفح افتح موقع MIT App Inventor.

أنشئ حساب على الموقع ثم انقر علي Create Apps.

التحكم بالحاسوب عن بعد

من قائمة Project اختر Import projects (.aia) from my computer ثم انقر على الملف الذي حملته مسبقًا ويحتوي على واجهة المستخدم والكود البرمجي.

التحكم بالحاسوب عن بعد

من قائمة Connect اختر Al companion

التحكم بالحاسوب عن بعد

سيظهر كود ورمز مكون من 6 حروف.

حمّل تطبيق MIT App Inventor على جهازك الذكي.

يمكنك ادخال الرمز في المستطيل أو النقر على scan QR code ومسح الكود السابق.

ثم انقر على connect with code.
التحكم بالحاسوب عن بعد

ستظهر هذه الواجهة انقر على Bluetooth Connect.smartphone-controlled-mouse

اختر نوع وحدة البلوتوث من القائمة النوع المستخدم في هذا الدرس HC-06.التحكم بالحاسوب عن بعد

ستظهر كلمة Connected في حال اكتمال عملية الاتصال.

شرح لطريقة استعمال الواجهة.

التحكم بالحاسوب عن بعد

كود البايثون

في البداية عليك تنصب برنامج Python 3 يمكنك الرجوع للدرس التالي لمعرفة كيفية تنصيبه تنصيب Python 3.

انقر بالسهم اليمين على Command Prompt واختر من القائمة Run as administrator.

التحكم بالحاسوب عن بعد

ادخل على مسار برنامج البايثون.

(هذا السطر قابل للتغيير بناء على موقع برنامج البايثون في جهازك)

cd C:\Program Files (x86)\Python39-32

حدّث Pip من خلال كتابة الأمر التالي.

python -m pip install --upgrade pip

حمّل مكتبة فأرة الحاسوب.

python –m pip install mouse

حمّل مكتبة pyserial.

python -m pip install pyserial

افتح برنامج IDLE (Python 3.9 32-bit) من قائمة File اختر New File التالي.

smartphone-controlled-mouse

الصق الكود البرمجي وارفعه للوحة الاردوينو من قائمة Run انقر على Run module.

import mouse, sys
import time 
import serial

mouse.FAILSAFE=False
ArduinoSerial=serial.Serial('com3',9600)  #Specify the correct COM port
time.sleep(1)                             #delay of 1 second

while 1:
   data=str(ArduinoSerial.readline().decode('ascii'))
   (x,y,z)=data.split(":")  # read the x and y axis data
   (x,y)=(int(x),int(y))   # convert to int
   mouse.move(x,y)         # move the cursor to desired coordinates
   if '1' in z:                       
      mouse.click(button="left")     #clicks mouse button
   elif '2' in z:
      mouse.click(button="right")
   elif '3' in z:
      mouse.wheel(delta=-1)       # Scroll down
   elif '4' in z:
      mouse.wheel(delta=1)       # Scroll up
  

شرح الكود البرمجي

افتح صفحة جديدة في IDLE Python وقم باستدعاء المكتبات المطلوبة mouse, sys serial python و time.

import mouse, sys
import time 
import serial

حدد المنفذ COM المستخدم في الاتصال مع الاردوينو، لديك وقم بتعديل الأمر في السطر التالي حسب رقم المنفذ.

(يمكنك تعيينه عن طريق برنامج اردوينو IDE من قائمة Port).

ArduinoSerial=serial.Serial('com3',9600)  #Specify the correct COM port

هنا تتم قراءة القيم من المنافذ التناظرية A0 و A1 وسيتم تحريك السهم في أماكن مختلفة على الشاشة بناء على هذه القيم.

while 1:
   data=str(ArduinoSerial.readline().decode('ascii'))
   (x,y,z)=data.split(":")  # read the x and y axis data
   (x,y)=(int(x),int(y))   # convert to int
   mouse.move(x,y)         # move the cursor to desired coordinates

بناء على القراءات سيتم تحديد حركة السهم إما للأعلى أو للأسفل أو لليمين أو لليسار.

   if '1' in z:                       
      mouse.click(button="left")     #clicks mouse button
   elif '2' in z:
      mouse.click(button="right")
   elif '3' in z:
      mouse.wheel(delta=-1)       # Scroll down
   elif '4' in z:
      mouse.wheel(delta=1)       # Scroll up

يمكنك التحكم بالحاسوب عن بعد باستخدام الاردوينو وهاتفك الذكي بعد رفع الكود البرمجي.

اختبر صحة خطواتك.

 لا تنسَ فصل مصدر الطاقة بعد الانتهاء من استخدام النظام.




مقياس تدفق الماء و التحكم بالكمية باستخدام الاردوينو

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

تدفق الماء


المواد و الأدوات*

اردوينو

X1 أردوينو أونو 

مرحل

X1 مرحل 

سلك الاردوينو

X1 سلك الأردوينو 

مفتاح

X8 ضغاط التحكم 

لوحة تجارب

X2 لوحة تجارب 

حساس التدفق

X1 حساس التدفق 

مقاومة

X8 مقاومة 10 كيلو أوم

مقاومة 220

1X مقاومة 220 أوم

مقاومة متغيرة

X1 مقاومة متغيرة 

صمام ملف

X1 ملف صمام 

وصلة  تيار ثابت

X1 وصلة تيار ثابت 

محول طاقة

X1 مصدر طاقة 24 فولت

توصيل الدائرة

توصيل الدائرة

 

حساس التدفق

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

يعتمد مستشعر التدفق مبدأ تأثير هول (Hall).
حيث يتكون المستشعر من مستشعر تأثير القاعدة (Hall Effect) وعجلة التوربين والمغناطيس. يتدفق الماء من خلال المدخل ويخرج من خلال المخرج. دفع تيار الماء العجلة إلى الدوران، وتدار معها المغناطيس الموجود على العجلة. يؤدي دوران المجال المغناطيسي إلى تشغيل مستشعر القاعة، والذي ينتج موجات مربعة عالية ومنخفضة المستوى (نبض).
لكل جولة من العجلة، يكون حجم المياه المتدفقة مقدارًا معينًا، وكذلك عدد الموجات المربعة الناتجة. لذلك، يمكننا حساب تدفق المياه عن طريق حساب عدد الموجات المربعة (النبض).

مستشعر التدفق

ويحتوي المستشعر على 3 أسلاك باللون الأحمر والأصفر والأسود. يستخدم السلك الأحمر للجهد الذي يتراوح من 5 فولت إلى 18 فولت والسلك الأسود متصل بـ GND. يستخدم السلك الأصفر للإخراج (نبضات)، والتي يمكن قراءتها بواسطة المتحكم ، كذلك يظهر على المستشعر سهم يشير إلى مسار الماء

مستشعر التدفق

فكرة المشروع

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

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

ثانيًا: مفتاح تحديد الوحدة يمكن قياس الكمية بوحدة مليلتر أو لتر، إذا كان في حالة 0يعني أنه على وحدة مليلتر. وحالة 1 يعني لتر

ثالثا: متاح إعادة ضبط الكمية المعينة إلى 0، حيث تُظهر الشاشة SP   والتي تعني الكمية التي يعمل النظام للوصول لها والتي تم تحديدها من قبل المستخدم.

رابعًا: زر إعادة ضبط عداد الكمية المعبئة إلى 0، لبدء تعبئة جديدة، حيث تظهر الشاشة CNT والتي تشير لكمية الماء التي تم تعبئتها

خامسًا: مفاتيح تعين كمية الماء، وهي أربع مفاتيح +1,+10,+100,+1000.

 

تحديد عدد نبضات لتر واحد من الماء

 مواصفات المنتج أن 450 نبضة للتر واحد من الماء لكن للوصول لقراءة دقيقة سنقوم بعملية الوزن ،عن طريق أخذ قراءة عدد النبضات عند تمرير لتر واحد من الماء

الكود البرمجي 

#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 10, 11, 12, 13); //Pines arduino to lcd

//-------Pins-----//
int Relay = 1; //Solenoid valve open/close
int start_stop = 2; //Start/Stop button
const int sensor_pulse = 16; //Sensor pulse
int rst_cnt = 3; // Reset counter button
//---------Storage debounce function-----//
boolean currentstart_stop = LOW; 
boolean laststart_stop =LOW; 
boolean lastsensor_pulse = LOW;
boolean currentsensor_pulse = LOW;
boolean lastrst_cnt = LOW;
boolean currentrst_cnt = LOW;
boolean RelayState = LOW; 

int counter = 0;

void setup() {
pinMode(Relay, OUTPUT);
lcd.begin(16, 2);
lcd.setCursor(5, 0);
lcd.print("COUNTER");
lcd.setCursor(0, 1);
lcd.print("PULSES");

}
//----Debouncing function----//
boolean debounce(boolean last, int pin)
{
boolean current = digitalRead(pin);
if (last != current)
{
delay(5);
current = digitalRead(pin);
}
return current;
}

void loop() { 
currentstart_stop = debounce(laststart_stop, start_stop); //Debounce for Start/Stop Button
currentsensor_pulse = debounce(lastsensor_pulse, sensor_pulse); //Debounce for Sensor pulse Button
currentrst_cnt = debounce(lastrst_cnt, rst_cnt); //Debounce for reset counter Button

//-----Start/Stop toggle function----//
if (currentstart_stop == HIGH && laststart_stop == LOW){

if (RelayState == HIGH){ //Toggle the state of the Relay
digitalWrite(Relay, LOW);
RelayState = LOW;


} 
else{
digitalWrite(Relay, HIGH);
RelayState = HIGH;
}
}

laststart_stop = currentstart_stop;

if (lastsensor_pulse== LOW && currentsensor_pulse == HIGH){
counter=counter+ 1;
}
lastsensor_pulse = currentsensor_pulse;

lcd.setCursor(7, 1);
lcd.print(counter);

if(RelayState == LOW){ //Reset counter while sistem is not running
if (currentrst_cnt == HIGH && lastrst_cnt == LOW){//Reset Counter
lcd.setCursor(6, 1); // Clear CNT area
lcd.print(" ");
counter= 0; 

}
lastrst_cnt = currentrst_cnt; 
}

}// end void loop

شرح الكود البرمجي 

استدعاء مكتبة الشاشة الكرستالية، و تعريف المنافذ للشاشة

 #include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 10, 11, 12, 13); //Pines arduino to lcd

تعريف منافذ الحساس تم توصيله مع منفذ A2= 16، و تسمية المتغير sensor_pulse، كذلك تعريف منفذ مفتاح التحكم بتشغيل النظام و هو المنفذ رقم 3 و منفذ مفتاح التحكم بتصفير العداد و هو المنفذ رقم 2

//-------Pins-----//
int Relay = 17; //Solenoid valve open/close
int start_stop = 2; //Start/Stop button
const int sensor_pulse = 16; //Sensor pulse
int rst_cnt = 3; // Reset counter button

تعين الحالة الابتدائية للمتغرات

boolean currentstart_stop = LOW; 
boolean laststart_stop =LOW; 
boolean lastsensor_pulse = LOW;
boolean currentsensor_pulse = LOW;
boolean lastrst_cnt = LOW;
boolean currentrst_cnt = LOW;
boolean RelayState = LOW; 

int counter = 0;

في دالة void setup نقوم بتهيئة الشاشة وتعريف منافذ الادخال و الاخراج

void setup() {
pinMode(Relay, OUTPUT);
lcd.begin(16, 2);
lcd.setCursor(5, 0);
lcd.print("COUNTER");
lcd.setCursor(0, 1);
lcd.print("PULSES");

}
//----Debouncing function----//
boolean debounce(boolean last, int pin)
{
boolean current = digitalRead(pin);
if (last != current)
{
delay(5);
current = digitalRead(pin);
}
return current;
}

في دالة void loop يتم حساب عدد النبضات و عرضها على الشاشة الكرستالية

void loop() { 
currentstart_stop = debounce(laststart_stop, start_stop); //Debounce for Start/Stop Button
currentsensor_pulse = debounce(lastsensor_pulse, sensor_pulse); //Debounce for Sensor pulse Button
currentrst_cnt = debounce(lastrst_cnt, rst_cnt); //Debounce for reset counter Button

//-----Start/Stop toggle function----//
if (currentstart_stop == HIGH && laststart_stop == LOW){

if (RelayState == HIGH){ //Toggle the state of the Relay
digitalWrite(Relay, LOW);
RelayState = LOW;


} 
else{
digitalWrite(Relay, HIGH);
RelayState = HIGH;
}
}

laststart_stop = currentstart_stop;
if (lastsensor_pulse== LOW && currentsensor_pulse == HIGH){
counter=counter+ 1;
}
lastsensor_pulse = currentsensor_pulse;
lcd.setCursor(7, 1);
lcd.print(counter);
if(RelayState == LOW){ //Reset counter while sistem is not running
if (currentrst_cnt == HIGH && lastrst_cnt == LOW){//Reset Counter
lcd.setCursor(6, 1); // Clear CNT area
lcd.print(" ");
counter= 0; 
}
lastrst_cnt = currentrst_cnt; 
}
}// end void loop

قم بحفض القيمة لكل لتر من الماء ليتم تعديلها في الشفرة البرمجية للمشروع

الكود البرمجي

#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 10, 11, 12, 13); //Pines arduino to lcd

//-------Pins-----//
int Relay = 17; //Solenoid valve open/close
int start_stop = 2; //Start/Stop button
int rst_sp = 3; // Reset Set Point Button
int rst_cnt = 4; // Reset counter button
int unit = 5; // Change Unit Button
const int sensor_pulse =16; // Sensor Pulse In
//----Analog as Input-----//
int add_one =6; // +1 Button
int add_ten = 7; // +10 Button
int add_cien = 14; // +100 Button
int add_mil = 15; // +1000 Buton

//-----Variables for debouncing-----//
boolean currentstart_stop = LOW; 
boolean laststart_stop =LOW; 
boolean lastsensor_pulse = LOW;
boolean currentsensor_pulse = LOW;
boolean lastunit = LOW;
boolean currentunit = LOW;
boolean lastrst_sp = LOW;
boolean currentrst_sp = LOW;
boolean lastrst_cnt = LOW;
boolean currentrst_cnt = LOW;
boolean lastadd_one = LOW;
boolean currentadd_one = LOW;
boolean lastadd_ten = LOW;
boolean currentadd_ten = LOW;
boolean lastadd_cien = LOW;
boolean currentadd_cien = LOW;
boolean lastadd_mil = LOW;
boolean currentadd_mil = LOW;

//-----Storage state for toggle function---//
boolean unitState = LOW; //storage for the current state of the unit
boolean RelayState = LOW; //storage for the current state of the Relay (off/on)

//-------You have to put your pulses x liters here-----//
float cal_1=2.5; //Calibrate ml x pulse (cal_1 = 1000/400)
int cal_2= 400; //Calibrate pulses x liters
//-----------------------------------------------------//

float counter_1 = 0.0; 
int counter_2= 0; 
int TotalCount_1= 0;
int TotalCount_2= 0;
int set_point_1= 0; 
int set_point_2= 0;

void setup(){
lcd.begin(16, 2);
pinMode(Relay, OUTPUT); 
pinMode(add_one, INPUT); 
pinMode(add_ten, INPUT); 
pinMode(add_cien, INPUT); 
pinMode(add_mil, INPUT);

lcd.setCursor(0,0); //Show "SP" on the LCD
lcd.print("SP"); 
lcd.setCursor(0,1); //Show "CNT" on the LCD
lcd.print("CNT"); 

}
//----Debouncing function for all buttons----//
boolean debounce(boolean last, int pin)
{
boolean current = digitalRead(pin);
if (last != current)
{
delay(5);
current = digitalRead(pin);
}
return current;
}

void loop(){
//-----Debounce Buttons-----//
currentstart_stop = debounce(laststart_stop, start_stop); //Debounce for Start/Stop Button
currentsensor_pulse = debounce(lastsensor_pulse, sensor_pulse); //Debounce for Sensor
currentunit = debounce(lastunit, unit); //Debounce for unit Button
currentrst_sp = debounce(lastrst_sp, rst_sp); //Debounce for reset set point Button
currentrst_cnt = debounce(lastrst_cnt, rst_cnt); //Debounce for reset counter Button
currentadd_one = debounce(lastadd_one, add_one); //Debounce for +1 Button
currentadd_ten = debounce(lastadd_ten, add_ten); //Debounce for +10 Button
currentadd_cien = debounce(lastadd_cien, add_cien); //Debounce for +100 Button
currentadd_mil = debounce(lastadd_mil, add_mil); //Debounce for +1000 Button


//-----Start/Stop toggle function----//
if (currentstart_stop == HIGH && laststart_stop == LOW){

if (RelayState == HIGH){ //Toggle the state of the Relay
digitalWrite(Relay, LOW);
RelayState = LOW;
} 
else{
digitalWrite(Relay, HIGH);
RelayState = HIGH;
}
}

laststart_stop = currentstart_stop;

//-------Unit toggle function----//
if(RelayState == LOW){ //You only can change unit while system is not running! 

//------ Lt/ml unit toggle function----//
if (currentunit == HIGH && lastunit == LOW){
lcd.setCursor(4, 1); //Clear lcd(CNT area) between unit change,keeping last count
lcd.print(" ");
lcd.setCursor(3,0); //Clear lcd (SP area) between unit change, keeping last SP
lcd.print(" ");

if (unitState == HIGH){ //Toggle the state of the unit (L/ml)
digitalWrite(unit, LOW);
unitState = LOW;
}
else{
digitalWrite(unit, HIGH);
unitState = HIGH; 
}
}
lastunit = currentunit;
}
//------Print unit state-----//
if(unitState==HIGH){ //Unit state HIGH = L 
lcd.setCursor(14,0);
lcd.print("Lt");
lcd.setCursor(14, 1);
lcd.print("Lt");

}
else { //Unit state LOW = ml
lcd.setCursor(14,0);
lcd.print("Ml");
lcd.setCursor(14,1);
lcd.print("Ml");
}//End Print unit state

//--------------------------// 
//------------Ml Counter-----//
//---------------------------//
if(unitState==LOW){ // LOW= Ml state

//-----------------------//
//-----Settings----------//
//----------------------//

if(RelayState == LOW){ // You only can change settings while system is not running!

//-----Adders Buttons (set point_1)---//

if (currentadd_ten == HIGH && lastadd_ten == LOW){ // Add +10
set_point_1 = set_point_1 +10;
}
lastadd_ten = currentadd_ten;

if (currentadd_cien == HIGH && lastadd_cien == LOW){ // Add +100
set_point_1 = set_point_1 +100;
}
lastadd_cien = currentadd_cien;

if (currentadd_mil == HIGH && lastadd_mil == LOW){ // Add +1000
set_point_1 = set_point_1 +1000;
}
lastadd_mil = currentadd_mil; 

//-------Reset Buttons----//
if (currentrst_sp == HIGH && lastrst_sp == LOW){ //Reset Set Point
lcd.setCursor(3, 0); // Clear SP area
lcd.print(" ");
set_point_1 = 0;
}
lastrst_sp = currentrst_sp;
if (currentrst_cnt == HIGH && lastrst_cnt == LOW){//Reset Counter
lcd.setCursor(4, 1); // Clear CNT area
lcd.print(" ");
counter_1= 0; 
TotalCount_1= 0;
}
lastrst_cnt = currentrst_cnt;
}//-----End Settings-----//

//----Start Counter------//
if(RelayState == HIGH){ // Only counts while relay is HIGH
if (lastsensor_pulse== LOW && currentsensor_pulse == HIGH){
counter_1 = counter_1 + cal_1;
}
}
lastsensor_pulse = currentsensor_pulse;

//-------Counter function-----//
if(counter_1 >= 10){
TotalCount_1 = TotalCount_1 + 10;
counter_1=0; //Counter reset
}

lcd.setCursor(3, 0); //Show set point
lcd.print(set_point_1);
lcd.setCursor(4, 1); // Show counter
lcd.print(TotalCount_1);

//--Stop Counter.You can´t start if set point is lower or equal to counter--//
if(RelayState==HIGH){
if(set_point_1 <= TotalCount_1){ 
RelayState = LOW;
digitalWrite(Relay, LOW);
//***********************************************Autoreset
lcd.setCursor(4, 1); // Clear CNT area
lcd.print(" ");
counter_1= 0; 
TotalCount_1= 0; 
}
}
}//End unit state LOW (ml)

//--------------------------// 
//------------Lt Counter-----//
//---------------------------//

if(unitState== HIGH){ //HIGH = Lt state

//-----------------------//
//-----Settings----------//
//----------------------//

if(RelayState == LOW){ // You only can change settings while system is not running!

//-----Adders Buttons (set point_2)---//
if (currentadd_one == HIGH && lastadd_one == LOW){ // Add +1 
set_point_2 = set_point_2 +1;
}
lastadd_one = currentadd_one;

if (currentadd_ten == HIGH && lastadd_ten == LOW){ // Add +10
set_point_2 = set_point_2 +10;
}
lastadd_ten = currentadd_ten;

if (currentadd_cien == HIGH && lastadd_cien == LOW){ // Add +100
set_point_2 = set_point_2 +100;
}
lastadd_cien = currentadd_cien;

if (currentadd_mil == HIGH && lastadd_mil == LOW){ // Add +1000
set_point_2 = set_point_2 +1000;
}
lastadd_mil = currentadd_mil; 

//-------Reset Buttons----//
if (currentrst_sp == HIGH && lastrst_sp == LOW){ //Reset Set Point
lcd.setCursor(3, 0); // Clear SP area
lcd.print(" ");
set_point_2 = 0;
}
lastrst_sp = currentrst_sp;
if (currentrst_cnt == HIGH && lastrst_cnt == LOW){//Reset Counter
lcd.setCursor(4, 1); // Clear CNT area
lcd.print(" ");
counter_2= 0; 
TotalCount_2= 0;
}
lastrst_cnt = currentrst_cnt;
}//-----End Settings-----//

//----Start Counter------//
if(RelayState == HIGH){ // Only counts while relay is HIGH
if (lastsensor_pulse== LOW && currentsensor_pulse == HIGH){
counter_2 = counter_2 + 1;
}
}
lastsensor_pulse = currentsensor_pulse;

//-------Counter function-----//
if(counter_2 == cal_2){
TotalCount_2 = TotalCount_2 + 1;
counter_2 = 0; //Counter reset
}

lcd.setCursor(3, 0); //Show set point
lcd.print(set_point_2);
lcd.setCursor(4, 1); // Show counter
lcd.print(TotalCount_2);

//--Stop Counter.You can´t start if set point is lower or equal to counter--//
if(RelayState==HIGH){
if(set_point_2 <= TotalCount_2){
RelayState = LOW;
digitalWrite(Relay, LOW);
//*****************************Autoreset
lcd.setCursor(4, 1); // Clear CNT area
lcd.print(" ");
counter_2= 0; 
TotalCount_2= 0; 
} 
}


}//End unit state HIGH (L)




}//End Void Loop

شرح الكود البرمجي

استدعاء مكتبة الشاشة الكرستالية و تعريف منافذها

#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 10, 11, 12, 13); //Pines arduino to lcd

تعريف منافذ مفاتيح النظام

//-------Pins-----//
int Relay = 17; //Solenoid valve open/close
int start_stop = 2; //Start/Stop button
int rst_sp = 3; // Reset Set Point Button
int rst_cnt = 4; // Reset counter button
int unit = 5; // Change Unit Button
const int sensor_pulse =16; // Sensor Pulse In
//----Analog as Input-----//
int add_one =6; // +1 Button
int add_ten = 7; // +10 Button
int add_cien = 14; // +100 Button
int add_mil = 15; // +1000 Buton

تعريف متغيرات

//-----Variables for debouncing-----//
boolean currentstart_stop = LOW; 
boolean laststart_stop =LOW; 
boolean lastsensor_pulse = LOW;
boolean currentsensor_pulse = LOW;
boolean lastunit = LOW;
boolean currentunit = LOW;
boolean lastrst_sp = LOW;
boolean currentrst_sp = LOW;
boolean lastrst_cnt = LOW;
boolean currentrst_cnt = LOW;
boolean lastadd_one = LOW;
boolean currentadd_one = LOW;
boolean lastadd_ten = LOW;
boolean currentadd_ten = LOW;
boolean lastadd_cien = LOW;
boolean currentadd_cien = LOW;
boolean lastadd_mil = LOW;
boolean currentadd_mil = LOW;

//-----Storage state for toggle function---//
boolean unitState = LOW; //storage for the current state of the unit
boolean RelayState = LOW; //storage for the current state of the Relay (off/on)

هنا يتم تعديل الأمر البرمجي بناء على عدد النبضات التي وجدتها في كود الموازنة، حيث تقوم بقسمة عدد النبضات على 1000 لتعين متغير cal_1 و متغسر cal_2 يساوي عدد النبضات

//-------You have to put your pulses x liters here-----//
float cal_1=2.5; //Calibrate ml x pulse (cal_1 = 1000/400)
int cal_2= 400; //Calibrate pulses x liters
//-----------------------------------------------------//

float counter_1 = 0.0;
int counter_2= 0;
int TotalCount_1= 0;
int TotalCount_2= 0;
int set_point_1= 0;
int set_point_2= 0;

في دالة void setup يتم تهيئة الشاشة

void setup(){
lcd.begin(16, 2);
pinMode(Relay, OUTPUT);
pinMode(add_one, INPUT);
pinMode(add_ten, INPUT);
pinMode(add_cien, INPUT);
pinMode(add_mil, INPUT);

lcd.setCursor(0,0); //Show "SP" on the LCD
lcd.print("SP");
lcd.setCursor(0,1); //Show "CNT" on the LCD
lcd.print("CNT");

}
//----Debouncing function for all buttons----//
boolean debounce(boolean last, int pin)
{
boolean current = digitalRead(pin);
if (last != current)
{
delay(5);
current = digitalRead(pin);
}
return current;
}


دالة الvoid loop لبدء النظام

 

void loop(){
//-----Debounce Buttons-----//
currentstart_stop = debounce(laststart_stop, start_stop); //Debounce for Start/Stop Button
currentsensor_pulse = debounce(lastsensor_pulse, sensor_pulse); //Debounce for Sensor
currentunit = debounce(lastunit, unit); //Debounce for unit Button
currentrst_sp = debounce(lastrst_sp, rst_sp); //Debounce for reset set point Button
currentrst_cnt = debounce(lastrst_cnt, rst_cnt); //Debounce for reset counter Button
currentadd_one = debounce(lastadd_one, add_one); //Debounce for +1 Button
currentadd_ten = debounce(lastadd_ten, add_ten); //Debounce for +10 Button
currentadd_cien = debounce(lastadd_cien, add_cien); //Debounce for +100 Button
currentadd_mil = debounce(lastadd_mil, add_mil); //Debounce for +1000 Button

//-----Start/Stop toggle function----//
if (currentstart_stop == HIGH && laststart_stop == LOW){

if (RelayState == HIGH){ //Toggle the state of the Relay
digitalWrite(Relay, LOW);
RelayState = LOW;
}
else{
digitalWrite(Relay, HIGH);
RelayState = HIGH;
}
}

laststart_stop = currentstart_stop;

//-------Unit toggle function----//
if(RelayState == LOW){ //You only can change unit while system is not running!

//------ Lt/ml unit toggle function----//
if (currentunit == HIGH && lastunit == LOW){
lcd.setCursor(4, 1); //Clear lcd(CNT area) between unit change,keeping last count
lcd.print(" ");
lcd.setCursor(3,0); //Clear lcd (SP area) between unit change, keeping last SP
lcd.print(" ");

if (unitState == HIGH){ //Toggle the state of the unit (L/ml)
digitalWrite(unit, LOW);
unitState = LOW;
}
else{
digitalWrite(unit, HIGH);
unitState = HIGH;
}
}
lastunit = currentunit;
}

الشرط التالي لتغير الوحدة

//------Print unit state-----//
if(unitState==HIGH){ //Unit state HIGH = L
lcd.setCursor(14,0);
lcd.print("Lt");
lcd.setCursor(14, 1);
lcd.print("Lt");

}
else { //Unit state LOW = ml
lcd.setCursor(14,0);
lcd.print("Ml");
lcd.setCursor(14,1);
lcd.print("Ml");
}//End Print unit state

//--------------------------//
//------------Ml Counter-----//
//---------------------------//
if(unitState==LOW){ // LOW= Ml state

//-----------------------//
//-----Settings----------//
//----------------------//

يمكن تغير الإعدادات اذا كان النظام لايعمل

if(RelayState == LOW){ // You only can change settings while system is not running!

//-----Adders Buttons (set point_1)---//

if (currentadd_ten == HIGH && lastadd_ten == LOW){ // Add +10
set_point_1 = set_point_1 +10;
}
lastadd_ten = currentadd_ten;

if (currentadd_cien == HIGH && lastadd_cien == LOW){ // Add +100
set_point_1 = set_point_1 +100;
}
lastadd_cien = currentadd_cien;

if (currentadd_mil == HIGH && lastadd_mil == LOW){ // Add +1000
set_point_1 = set_point_1 +1000;
}
lastadd_mil = currentadd_mil;

الشرط التالي لتحديد عمل مفتاح اعادة الضبط

//-------Reset Buttons----//
if (currentrst_sp == HIGH && lastrst_sp == LOW){ //Reset Set Point
lcd.setCursor(3, 0); // Clear SP area
lcd.print(" ");
set_point_1 = 0;
}
lastrst_sp = currentrst_sp;
if (currentrst_cnt == HIGH && lastrst_cnt == LOW){//Reset Counter
lcd.setCursor(4, 1); // Clear CNT area
lcd.print(" ");
counter_1= 0;
TotalCount_1= 0;
}
lastrst_cnt = currentrst_cnt;
}//-----End Settings-----//

//----Start Counter------//
if(RelayState == HIGH){ // Only counts while relay is HIGH
if (lastsensor_pulse== LOW && currentsensor_pulse == HIGH){
counter_1 = counter_1 + cal_1;
}
}
lastsensor_pulse = currentsensor_pulse;

دالة العداد

//-------Counter function-----//
if(counter_1 >= 10){
TotalCount_1 = TotalCount_1 + 10;
counter_1=0; //Counter reset
}

lcd.setCursor(3, 0); //Show set point
lcd.print(set_point_1);
lcd.setCursor(4, 1); // Show counter
lcd.print(TotalCount_1);

//--Stop Counter.You can´t start if set point is lower or equal to counter--//
if(RelayState==HIGH){
if(set_point_1 <= TotalCount_1){
RelayState = LOW;
digitalWrite(Relay, LOW);
//***********************************************Autoreset
lcd.setCursor(4, 1); // Clear CNT area
lcd.print(" ");
counter_1= 0;
TotalCount_1= 0;
}
}
}//End unit state LOW (ml)

//--------------------------//
//------------Lt Counter-----//
//---------------------------//

if(unitState== HIGH){ //HIGH = Lt state

//-----------------------//
//-----Settings----------//
//----------------------//

if(RelayState == LOW){ // You only can change settings while system is not running!

//-----Adders Buttons (set point_2)---//
if (currentadd_one == HIGH && lastadd_one == LOW){ // Add +1
set_point_2 = set_point_2 +1;
}
lastadd_one = currentadd_one;

if (currentadd_ten == HIGH && lastadd_ten == LOW){ // Add +10
set_point_2 = set_point_2 +10;
}
lastadd_ten = currentadd_ten;

if (currentadd_cien == HIGH && lastadd_cien == LOW){ // Add +100
set_point_2 = set_point_2 +100;
}
lastadd_cien = currentadd_cien;

if (currentadd_mil == HIGH && lastadd_mil == LOW){ // Add +1000
set_point_2 = set_point_2 +1000;
}
lastadd_mil = currentadd_mil;

//-------Reset Buttons----//
if (currentrst_sp == HIGH && lastrst_sp == LOW){ //Reset Set Point
lcd.setCursor(3, 0); // Clear SP area
lcd.print(" ");
set_point_2 = 0;
}
lastrst_sp = currentrst_sp;
if (currentrst_cnt == HIGH && lastrst_cnt == LOW){//Reset Counter
lcd.setCursor(4, 1); // Clear CNT area
lcd.print(" ");
counter_2= 0;
TotalCount_2= 0;
}
lastrst_cnt = currentrst_cnt;
}//-----End Settings-----//

//----Start Counter------//
if(RelayState == HIGH){ // Only counts while relay is HIGH
if (lastsensor_pulse== LOW && currentsensor_pulse == HIGH){
counter_2 = counter_2 + 1;
}
}
lastsensor_pulse = currentsensor_pulse;

//-------Counter function-----//
if(counter_2 == cal_2){
TotalCount_2 = TotalCount_2 + 1;
counter_2 = 0; //Counter reset
}

lcd.setCursor(3, 0); //Show set point
lcd.print(set_point_2);
lcd.setCursor(4, 1); // Show counter
lcd.print(TotalCount_2);

//--Stop Counter.You can´t start if set point is lower or equal to counter--//
if(RelayState==HIGH){
if(set_point_2 <= TotalCount_2){
RelayState = LOW;
digitalWrite(Relay, LOW);
//*****************************Autoreset
lcd.setCursor(4, 1); // Clear CNT area
lcd.print(" ");
counter_2= 0;
TotalCount_2= 0;
}
}

}//End unit state HIGH (L)




}//End Void Loop



عرض أوقات الصلاة على الشاشة باستخدام الاردوينو

مقدمة

في هذا الدرس ستتعلم كيف تعرض أوقات الصلاة على شاشة Oled باستخدام الاردوينو.arduino-smart-prayer-times-display

المواد والأدوات

أوقات الصلاة

1× اردوينو اونو

أوقات الصلاة

1× سلك الاردوينو

أوقات الصلاة

 حزمة أسلاك توصيل (ذكر- ذكر)

arduino-smart-prayer-times-display

1× وحدة الوقت الحقيقي (RTC)

arduino-smart-prayer-times-display

1× شاشة (OLED)


arduino-smart-prayer-times-display

لوحة تجارب حجم وسط

توصيل الدائرة

للمزيد حول الشاشة (OLED) يمكنك الرجوع للدرس  شاشة عرض (OLED) Display.

وللمزيد حول وحدة الوقت الحقيقي يمكنك الرجوع للدرس استخدام DS3231 RTC Module مع الاردوينو.arduino-smart-prayer-times-display

الكود البرمجي

بالبداية عليك تحميل المكتبات التالية.

 Streaming.h  و “mainroutines.h” و <U8g2lib.h>

اقرأ شرح الكود البرمجي قبل رفع الكود البرمجي إلى لوحة الاردوينو.

#include <Wire.h>
#include <RTClib.h>
#include <Streaming.h>
#include <SoftwareSerial.h>
#include <U8g2lib.h>
#include "mainroutines.h"

const unsigned int text1_y0=30, text2_y0=60, text2_x1=0, text2_x2=58;
char text1[8], text2[6], todaydate[9];
int displaypage=0;
const uint8_t BTTX = 3;
const uint8_t BTRX = 2;
int quartsec, OldDay, OldMinute, OldSecond, dayMinutes, NextSalat;
int i;
DateTime now;
char time[9];

U8G2_SSD1306_128X64_NONAME_2_HW_I2C u8g2(U8G2_R0, /* clock=*/ SCL, /* data=*/ SDA); 
SoftwareSerial BTSerial(BTRX, BTTX);

struct FLAGS {
unsigned Recalcule : 1;
unsigned CheckTime : 1;
unsigned seconde : 1;
unsigned SeqAthan : 1;
unsigned Toggles : 1;
unsigned demiseconde: 1;
unsigned heures : 1;
unsigned displayPageTog : 1;
};
struct FLAGS Flags;

void setup()
{
Serial.begin(115200);
BTSerial.begin(9600);
BTSerial.listen();
u8g2.begin();
u8g2.setFont(u8g2_font_logisoso24_tr);
u8g2.setFontMode(0);


u8g2.firstPage();
do {
u8g2.drawUTF8(0,text1_y0,"SalatTime");
u8g2.drawUTF8(0,text2_y0,"L. Baghli");
} 
while ( u8g2.nextPage() );

Wire.begin();
rtc.begin();
if (! rtc.isrunning()) {
Serial.println(F("RTC NOT running!"));
rtc.adjust(DateTime(2021,8, 24, 16,8, 0));
}

Serial.flush();
STinit();
Flags.Recalcule=1;
Flags.heures=0;
Flags.CheckTime=0;
Flags.displayPageTog=0;
cli();
TCCR1A = 0;
TCCR1B = (0<<WGM13) | (1<<WGM12) | 4;
OCR1A = 15625;
TIMSK1 = 1<<OCIE1A;
TIFR1 = 0;
TCNT1 = 0;
sei();
}
void CheckTime()
{
now = rtc.now();
Flags.Toggles = !Flags.Toggles;
if (OldSecond != now.second()) {
if (++displaypage>5) displaypage=0;
OldSecond = now.second();
Serial << now.day() << F("/")<< now.month() << F("/")<<now.year()
<< F(" ")<< now.hour() << F(":")<<now.minute() << F(":")<<now.second() << endl;
}
if (OldDay != now.day()) Flags.Recalcule = 1;
if ((OldMinute != now.minute()) && (Flags.Recalcule == 0))
{
if ((SalatT.m[NextSalat]==now.minute()) && (SalatT.h[NextSalat]==now.hour()))
{// Athan
Flags.SeqAthan = 1;
}
else Flags.SeqAthan = 0;
OldMinute = now.minute();
dayMinutes = now.hour()*60+now.minute();
for (i=4; i>=0; i--)
if (SalatT.h[i]*60 + SalatT.m[i] >= dayMinutes)
{
NextSalat = i;
Serial << F("SalatT.m[")<< i <<F("] =") << SalatT.m[i] << endl;
}
}
}
void MaJRTC(int * dt)
{
if ((dt[0]<1) || (dt[0]>31)) return;
if ((dt[1]<1) || (dt[1]>12)) return;
if ((dt[2]<2000) || (dt[2]>2099)) return;
if ((dt[3]<0) || (dt[3]>23)) return;
if ((dt[4]<0) || (dt[4]>59)) return;
if ((dt[5]<0) || (dt[5]>59)) return;
rtc.adjust(DateTime(dt[2], dt[1], dt[0], dt[3], dt[4], dt[5]));
}
ISR(TIMER1_COMPA_vect) {
if (Flags.SeqAthan)
{
Flags.Toggles = !Flags.Toggles;
}
if (++quartsec == 4) quartsec = 0;
Flags.CheckTime = 1; // check time chaque 1/4 seconde
Flags.displayPageTog = 1; // page affichée chaque 1/4 seconde
if (quartsec == 0) {
Flags.seconde = 1;
}
}
void DisplayPages()
{
switch(displaypage)
{
case 0: sprintf( text1, "Fajr");
sprintf( text2, "%02hhu:%02hhu", SalatT.h[0],SalatT.m[0] );
break;
case 1: sprintf( text1, "Duhr");
sprintf( text2, "%02hhu:%02hhu", SalatT.h[1],SalatT.m[1] );
break;
case 2: sprintf( text1, "Asr");
sprintf( text2, "%02hhu:%02hhu", SalatT.h[2],SalatT.m[2] );
break;
case 3: sprintf( text1, "Mgrb");
sprintf( text2, "%02hhu:%02hhu", SalatT.h[3],SalatT.m[3] );
break;
case 4: sprintf( text1, "Isha");
sprintf( text2, "%02hhu:%02hhu", SalatT.h[4],SalatT.m[4] );
break;
for (int i=0; i<8; i++) text1[i] = todaydate[i];
sprintf( text2, "");
break;
}
sprintf( time, "%02hhu:%02hhu:%02hhu", now.hour(), now.minute(), now.second() );
u8g2.firstPage();
do {
u8g2.drawUTF8(0,text1_y0,time);
u8g2.drawUTF8(text2_x1,text2_y0, text1);
u8g2.drawUTF8(text2_x2,text2_y0, text2);
} while ( u8g2.nextPage() );
}
void loop()
{
if (Flags.CheckTime) {
CheckTime();
Flags.CheckTime = 0;
}
if (Flags.Recalcule) {
ComputeSalatTime();
OldDay = now.day();
sprintf( todaydate, "%02hhu/%02hhu/%02hhu", now.day(),now.month(),now.year()%100 );
todaydate[8]=0;
OldMinute = 61;
OldSecond = 61;
Flags.Recalcule = 0;
}
if (Flags.displayPageTog) {
DisplayPages();
Flags.displayPageTog = 0;
}
}

شرح الكود البرمجي

هنا تم استدعاء المكتبات التي سنستخدمها لعرض أوقات الصلاة على الشاشة.

#include <Wire.h>
#include <RTClib.h>
#include <Streaming.h>
#include <SoftwareSerial.h>
#include <U8g2lib.h>
#include "mainroutines.h"

هنا يتم تعريف المتغيرات التي سنستخدمها لتخزين النص الذي سينطبع على الشاشة.

char text1[8], text2[6], todaydate[9];

ربط المنافذ في الشاشة OLED بحيث يكون المنفذ SCL مع المنفذ SCL الموجود في وحدة الوقت والمنفذ SDA مع المنفذ SDA في وحدة الوقت.

U8G2_SSD1306_128X64_NONAME_2_HW_I2C u8g2(U8G2_R0, /* clock=*/ SCL, /* data=*/ SDA);

يتم تقسيم الجهد 5V BTTX يحمل القيمة 3 و BTRX يحمل القيمة 2.

const uint8_t BTTX = 3;
const uint8_t BTRX = 2;

في الدالة ()setup يتم تهيئة الشاشة استعدادًا لطباعة الوقت وحساب أوقات الصلاة.

void setup()
{
Serial.begin(115200);
BTSerial.begin(9600);
BTSerial.listen();
u8g2.begin();
u8g2.setFont(u8g2_font_logisoso24_tr);
u8g2.setFontMode(0);
u8g2.firstPage();
do {
u8g2.drawUTF8(0,text1_y0,"SalatTime");
u8g2.drawUTF8(0,text2_y0,"L. Baghli");
} 
while ( u8g2.nextPage() );
Wire.begin();
rtc.begin();
if (! rtc.isrunning()) {
Serial.println(F("RTC NOT running!"));
rtc.adjust(DateTime(2021,8, 24, 16,8, 0));
}

 هذا السطر لابد من تعديله حسب التاريخ والوقت الحالي لديك و كتابة الوقت بالثواني و الدقائق و الساعة ، و التاريخ اليوم و الشهر والسنة

rtc.adjust(DateTime(2021,8, 24, 16,8, 0));  

هنا يتم تهيئة المتغيرات التي تستخدم لحساب أوقات الصلاة.

STinit();
Flags.Recalcule=1;
Flags.heures=0;
Flags.CheckTime=0;
Flags.displayPageTog=0;

في الدالة ()CheckTime يتم حساب الوقت وطباعته على الشاشة كل ثانية.

void CheckTime()
{
now = rtc.now();
Flags.Toggles = !Flags.Toggles;
if (OldSecond != now.second()) {
if (++displaypage>5) displaypage=0;
OldSecond = now.second();
Serial << now.day() << F("/")<< now.month() << F("/")<<now.year()
<< F(" ")<< now.hour() << F(":")<<now.minute() << F(":")<<now.second() << endl;
}
if (OldDay != now.day()) Flags.Recalcule = 1;
if ((OldMinute != now.minute()) && (Flags.Recalcule == 0))
{
if ((SalatT.m[NextSalat]==now.minute()) && (SalatT.h[NextSalat]==now.hour()))
{
Flags.SeqAthan = 1;
}
else Flags.SeqAthan = 0;
OldMinute = now.minute();
dayMinutes = now.hour()*60+now.minute();
for (i=4; i>=0; i--)
if (SalatT.h[i]*60 + SalatT.m[i] >= dayMinutes)
{
NextSalat = i;
Serial << F("SalatT.m[")<< i <<F("] =") << SalatT.m[i] << endl;
}
}
}

هنا يتم برمجة وحدة الوقت بحيث الساعات باليوم تكون 24 والدقائق 60 دقيقة للساعة و60 ثانية بالدقيقة.

void MaJRTC(int * dt)
{
if ((dt[0]<1) || (dt[0]>31)) return;
if ((dt[1]<1) || (dt[1]>12)) return;
if ((dt[2]<2000) || (dt[2]>2099)) return;
if ((dt[3]<0) || (dt[3]>23)) return;
if ((dt[4]<0) || (dt[4]>59)) return;
if ((dt[5]<0) || (dt[5]>59)) return;
rtc.adjust(DateTime(dt[2], dt[1], dt[0], dt[3], dt[4], dt[5]));
}
ISR(TIMER1_COMPA_vect) 
{
if (Flags.SeqAthan)
{
Flags.Toggles = !Flags.Toggles;
}
if (++quartsec == 4) quartsec = 0;
Flags.CheckTime = 1; 
Flags.displayPageTog = 1; 
if (quartsec == 0) {
Flags.seconde = 1;
}
}

هنا سيتم برمجة الشاشة لتحتوي على ست شاشات مختلفة في المحتوى.

الأولى: عرض موعد صلاة الفجر.

الثانية: عرض موعد صلاة الظهر.

الثالثة: عرض موعد صلاة العصر.

الرابعة: عرض صلاة المغرب.

الخامسة: عرض صلاة العشاء.

السادسة: عرض تاريخ اليوم.

void DisplayPages()
{
switch(displaypage)
{
case 0: sprintf( text1, "Fajr");
sprintf( text2, "%02hhu:%02hhu", SalatT.h[0],SalatT.m[0] );
break;
case 1: sprintf( text1, "Duhr");
sprintf( text2, "%02hhu:%02hhu", SalatT.h[1],SalatT.m[1] );
break;
case 2: sprintf( text1, "Asr");
sprintf( text2, "%02hhu:%02hhu", SalatT.h[2],SalatT.m[2] );
break;
case 3: sprintf( text1, "Mgrb");
sprintf( text2, "%02hhu:%02hhu", SalatT.h[3],SalatT.m[3] );
break;
case 4: sprintf( text1, "Isha");
sprintf( text2, "%02hhu:%02hhu", SalatT.h[4],SalatT.m[4] );
break;
for (int i=0; i<8; i++) text1[i] = todaydate[i];
sprintf( text2, "");
break;
}
sprintf( time, "%02hhu:%02hhu:%02hhu", now.hour(), now.minute(), now.second() );
u8g2.firstPage();
do {
u8g2.drawUTF8(0,text1_y0,time);
u8g2.drawUTF8(text2_x1,text2_y0, text1);
u8g2.drawUTF8(text2_x2,text2_y0, text2);
} while ( u8g2.nextPage() );
}

في الدالة ()loop سيتم تحديث الوقت وموعد الصلاة والتاريخ بشكل مستمر.

void loop()
{
if (Flags.CheckTime) {
CheckTime();
Flags.CheckTime = 0;
}
if (Flags.Recalcule) {
ComputeSalatTime();
OldDay = now.day();
sprintf( todaydate, "%02hhu/%02hhu/%02hhu", now.day(),now.month(),now.year()%100 );
todaydate[8]=0;
OldMinute = 61;
OldSecond = 61;
Flags.Recalcule = 0;
}
if (Flags.displayPageTog) {
DisplayPages();
Flags.displayPageTog = 0;
}
}

حمّل الملفات التالية التي تحتوي على الدوال المهمة؛ لحساب أوقات الصلاة بناء على موقعك الجغرافي.

mainroutines.cpp

و

mainroutines.h

انقر على Add File أضف الملفات اللذان قمت بتحميلهما.arduino-smart-prayer-times-displayبعد إضافتهما يمكنك التعديل عليهما اذهب إلى الملف mainroutines.cpp.

حرر الأسطر التالية لكي تناسب منطقة السكن لديك.

ادخل على هذا الرابط وضع منطقة السكن لديك (استخدم نسخة Expert).

عدّل []CountryName و []TownName و TimeZoneTown و Convention  و DST بناء على البيانات التي عرضها.

استخدم هذا الرابط لعرض خريطة منطقة السكن لديك.

انسخ وعدل قيم latitude و longitude الموجودة في الكود البرمجي.

في هذا الدرس استخدمنا البيانات الخاصة بمدينة الرياض.

/** change your town */
	const char CountryName[] PROGMEM =  "Arabie Saoudite";
	const char TownName[] PROGMEM = ""Ar Riyad";
	const double latitude = 24.6498255*deg2rd;
	const double longitude = 46.7687988*deg2rd;
	const int TimeZoneTown = 3;
	const int Convention =  5;
	const int DST = 0;

بعد تعديل الكود البرمجي يمكنك رفع الكود إلى لوحة الاردوينو.

يمكنك اختبار صحة خطواتك.

 لا تنسَ فصل مصدر الطاقة بعد الانتهاء من استخدام النظام.




فعّل مساعد جوجل الافتراضي على الراسبيري باي

مقدمة

يعمل مساعد جوجل بتقنيات الذكاء الاصطناعي، ويمكن للمستخدمين التفاعل مع مساعد جوجل بالأوامر الصوتية والبحث في الانترنت وترتيب المواعيد والأحداث، في هذا الدرس ستتعلم كيفية تفعيل مساعد جوجل الافتراضي على الراسبيري باي.

المواد والأدوات

google-assistant

 1× راسبيري باي

google-assistant

 1× سلك (HDMI)

مساعد جوجل

1× محول تيار (5V-2A)

مساعد جوجل

1× سلك ايثرنت

مساعد جوجل

 1× كرت ذاكرة

google-assistant

1× مايكروفون

google-assistant

1× مكبر صوت

تهيئة حساب جوجل

الخطوة الأولى قبل البدء في برمجة المساعد الافتراضي على الراسبيري باي تحتاج إلى تسجيل وإعداد مشروعك على منصة الأحداث الخاصة بقوقل (Actions on Google). وهي منصة تطوير لمساعد قوقل. تسمح لجهة خارجية بتطوير تطبيقات صغيرة لمساعد قوقل عن طريق توفر وظائف إضافية.

اتبع الخطوات التالية لتسجيل وإعداد مشروع على منصة الأحداث

قم بتسجيل الدخول إلى منصة الأحداث باستخدام حساب جيميل (Gmail)

انقر على مشروع جديد New project.

  مساعد جوجل

ستظهر لك نافذة الشروط والأحكام، وتحديد الدولة، اختر الموافقة والاستمرار  (Agree and continue).

google-assistant

ثم ستظهر نافذة لتحديد اسم المشروع وتعيين البلد واللغة، قم بكتابة اسم المشروع وعين اللغة والبلد وانقر على انشئ مشروعًا (Create project)

مساعد جوجل

بعد الانتهاء من إنشاء مشروع في منصة الأحداث ستحتاج لتمكين Google Embedded Assistant API بالخطوات التالية:

في نافذة تبويب جديدة انتقل إلى منصة جوجل السحابية.

قم بتحديد مشروعك الذي قمت بإنشائه في الخطوة السابقة عن طريق النقر على “Select project” ثم “All”

بعد التأكد من أنك حددت مشروعك انقرعلى تمكين Enable.

google-assistant

عليك أن تقوم بتسجيل نموذجك  لكي يستجيب مساعد قوقل للأوامر المناسبة لجهازك، يحتاج المساعد إلى معلومات حول جهازك. مثل نوع الجهاز والشركة المصنعة. يمكن تحديد الجهاز ضمن فئة عامة – مثل مصباح أو مكبر صوت أو لعبة روبوت

لتحديد جهازك عد إلى منصة الأحداث. ستجد في نهاية الصفحة ظهور الجملة التالية “Are you looking for device registration” انقر على Click here.

google-assistant

 

سيتم نقلك إلى النافذة التالية، انقر فوق الزر “Register Model” للمتابعة.

مساعد جوجل

سيتم نقلك إلى النافذة التالية والتي تتطلب اسم المنتج و الشركة المصنعة، يجب ملء الفراغات في النافذة، نحن كتبنا اسم و شركة مصنعة من اختيارانا

 من قائمة Device type اختر Speaker ثم انقر على (Register model).

google-assistant

بعد أن قمت بتسجيل النموذج ستحصل على ملف الاعتماد و الذي يسمح لك بإجراء اتصال بخدمة مساعد قوقل  قم بتنزيل الملف على جهازك  بالنقر على التحميل Download OAuth 2.0 credentials. ستحتاج إليه في قسم تهيئة الراسبيري باي ثم انقر على Next.

google-assistant

 

يمكنك أن تحدد السمات التي تحتاجها في مشروعك بوضع علامة  (صح) مقابل السمة التي يحتاجها المشروع أو جلب جميع السمات بوضع علامة صح أمام All 7 traits.

ثم انقر على Save Traits.

google-assistant عد إلى منصة جوجل السحابية.

اظهر قائمة التنقل من خلال النقر على الثلاث شرطات في أعلى يسار الصفحة.

ثم اختر APIs & Services وانقر على OAuth consent screen.

مساعد جوجل

السماح للمستخدمين باستعمال النظام.

انقر على External ثم Create.

google-assistant

من قائمة User support email اختر البريد الإلكتروني على Gmail.

google-assistant

انزل أسفل الصفحة دوّن البريد الإلكتروني مرة أخرى ثم انقر على Save and continue.

google-assistant

في الصفحة التالية انقر على Save and continue.

google-assistant

انقر على Save and continue.

google-assistant

انقر على Back to Dashboard.

google-assistant

انقر على Publish app ثم confirm.

google-assistant

افتح صفحة الأنشطة الخاصة بك على جوجل.

انقر على Web & App Activity.

google-assistant

فعّل Web & App Activity.

ثم انقر على Turn on.

مساعد جوجلمساعد جوجل

فعّل الخيارين التاليين وانقر على I agree للموافقة على الشروط والأحكام.

google-assistant

توصيل القطع

وصّل المايكروفون مع لوحة الراسبيري باي كما في الشكل.

google-assistant

وصّل مكبر الصوت مع لوحة الراسبيري باي كما في الشكل.

google-assistant

تهيئة الراسبيري باي

أولا، سوف تحتاج إلى تثبيت نظام الراسبيان على الراسبيري باي إذا لم تكن قد فعلت ذلك قم بالإطلاع على  الدرس الأول نظام تشغيل الراسبيري باي

وبعد تثبيت النظام، يمكنك تهيئة النظام من خلال الرجوع  للدرس الثاني تهيئة نظام التشغيل

افتح الشاشة السوداء LXterminal للبدء باستخدام الراسبيري باي.

مساعد جوجل

للبدء، قم بإستخدام الـ Termial لتحديث الراسبيري باي إلى أحدث إصدار.

sudo apt-get update
sudo apt-get upgrade

ثم أعد تشغيل الراسبيري باي.

reboot

انشيء مجلد جديد باسم googleassistant.

mkdir googleassistant

ادخل إلى المجلد googleassistant.

cd googleassistant

في هذا السطر سنقوم بتعديل الملف التالي credentials.json.

sudo nano credentials.json

افتح ملف OAuth 2.0 Credentials  الذي قمت بتحميله مسبقًا في قسم تهيئة حساب جوجل وانسخ محتواه والصقه في ملف credentials.json كما في الشكل.

google-assistant

اضغط من لوحة المفاتيح على زر Ctrl + x لحفظ التعديلات.

ثم اضغط على زر Y للتأكيد على حفظ التعديلات.

أخيرًا اضغط على الشاشة السوداء. Enter للعودة إلى

اكتب السطر التالي حتى تقوم بتحميل الحزمة اللازمة.

sudo apt install python3-dev python3-venv python3-pip libssl-dev libffi-dev libportaudio2

حمّل مكتبة Google assistant.

sudo python3 -m pip install --upgrade google-assistant-library

حمّل مكتبة google-assistant-sdk[samples].

sudo python3 -m pip install --upgrade google-assistant-sdk[samples]

حمّل مكتبة google-auth-oauthlib[tool].

sudo python3 -m pip install --upgrade google-auth-oauthlib[tool]

سنستخدم google-oauthlib-tool لتفعيل الكود والأدوات اللازمة.

google-oauthlib-tool --client-secrets credentials.json \
--scope https://www.googleapis.com/auth/assistant-sdk-prototype \
--scope https://www.googleapis.com/auth/gcm \
--save --headless

ستواجهك رسالة بهذا الشكل انسخ الرابط الذي في الأسفل والصقه في المتصفح.

مساعد جوجل

بعد ذلك سجل دخولك على البريد الإلكتروني Gmail.

انقر على السماح Allow.

google-assistant

 انقر على السماح Allow.

google-assistant

انقر على السماح Allow.

google-assistant

انسخ الكود الذي سيظهر لك بهذا الشكل.

google-assistant

الصق الكود أسفل الرابط كما في الشكل.

google-assistant

عد إلى منصة جوجل السحابية.

اتبع المسار التالي حتى تحصل على ID الخاص بمشروعك.

مساعد جوجل

عد إلى منصة الأحداث.

حتى تحصل على ID الخاص بالجهاز.

google-assistant

google-assistant

هذا السطر يحتاج تعديل.

<projectid> ضع مكانه ID الخاص بمشروعك.

<deviceid> ضع مكانه ID الخاص بالجهاز.

googlesamples-assistant-pushtotalk --project-id <projectid> --device-model-id <deviceid>

يمكنك الآن اختبار مساعد قوقل الخاص بك.

لا تنسَ فصل وحدة الطاقة بعد الانتهاء من استخدام النظام.

 




التحكم بالطابعة عن بعد باستخدام الراسبيري باي

مقدمة

 توجد العديد من الطابعات التي تدعم الاتصال بالإنترنت، وتسهل عملية إرسال الملفات لا سلكيًا إليها وتستقبل أوامر الطباعة عن بعد، بدون الحاجة إلى توصيلها مع جهاز الكمبيوتر، وهذه الميزة يمكن أن نوفرها في الطابعات السلكية التقليدية باستخدام الراسبيري باي .طابعة لاسلكية

المواد والأدوات

raspberry-pi-print-server

 1× راسبيري باي

raspberry-pi-print-server

 1× سلك (HDMI)

raspberry-pi-print-server

1× سلك ايثرنت

طابعة لاسلكية

1× محول تيار (5V-2A)

طابعة لاسلكية

 1× كرت ذاكرة

توصيل القطع

وصّل الطابعة مع لوحة الراسبيري باي كما في الشكل.

التحكم بالطابعة عن بعد

تهيئة الراسبيري باي

أولا، سوف تحتاج إلى تثبيت نظام الراسبيان على الراسبيري باي إذا لم تكن قد فعلت ذلك قم بالإطلاع على  الدرس الأول نظام تشغيل الراسبيري باي

وبعد تثبيت النظام، يمكنك تهيئة النظام من خلال الرجوع  للدرس الثاني تهيئة نظام التشغيل

افتح الشاشة السوداء LXterminal للبدء باستخدام الراسبيري باي.

التحكم بالطابعة عن بعد

للبدء، قم بإستخدام الـ Termial لتحديث الراسبيري باي إلى أحدث إصدار.

sudo apt-get update
sudo apt-get upgrade

ثم أعد تشغيل الراسبيري باي.

reboot

CUPS هو نظام طباعة مفتوح المصدر  يستخدم بروتوكول الطباعة عبر الإنترنت (IPP) لدعم الطباعة على الطابعات المتصلة بالشبكة.

حمّل CUPS على الراسبيري باي وذلك بكتابة الأمر التالي.

sudo apt install cups

في هذا الأمر سنقوم بإضافة اسم المستخدم للوحة الراسبيري باي لقائمة المستخدمين الذين يمكنهم استخدام الطابعات.

sudo usermod -a -G lpadmin pi

سنقوم بتحرير ملف الشبكة.

sudo nano /etc/dhcpcd.conf

اكتب الأمر التالي حتى يمكنك الحصول على عنوان الشبكة للراسبيري باي.

قم بنسخه ستحتاج إليه بالخطوة التالية.

sudo ifconfig

أضف الأسطر التالية في الملف ولكن عليك تغيير ip_address و routers و domain_name_servers إلى عنوان الشبكة الخاص بلوحة الراسبيري باي.

interface wlan0
static ip_address=192.168.100.14/24
static routers=192.168.100.1
static domain_name_servers=192.168.100.1

طابعة لاسلكية

انقر على crtl + x لحفظ الملف.

ثم انقر على حرف y.

ثم انقر Enter.

سنقوم بتهيئة CUPS للوصول إليه عبر الشبكة.

sudo cupsctl --remote-any

اكتب الأمر التالي.

sudo raspi-config

من القائمة اختر Interface options.التحكم بالطابعة عن بعد

من القائمة اختر SSH.التحكم بالطابعة عن بعد

تمكين SSH من خلال النقر على Yes.

التحكم بالطابعة عن بعد

تهيئة CUPS

من المتصفح اكتب السطر التالي في مربع البحث وغيّر عنوان الشبكة لعنوان الراسبيري باي.

192.168.100.14:631

ستظهر لك هذه الصفحة مباشرة.

سجل دخول باسم المستخدم وكلمة المرور للوحة الراسبيري باي.

التحكم بالطابعة عن بعد

انقر على Administration ثم Add Printer.

raspberry-pi-print-server

انقر على الخيار التالي لتخطي الصفحة واستكمال تثبيت الطابعة.
raspberry-pi-print-server

سيظهر اسم الطابعة الخاصة بك مباشرة انقر عليها.

ثم انقر على Continue.
التحكم بالطابعة عن بعد

انقر على Share This Printer ثم Continue.طابعة لاسلكية

اختر الموديل المطابق لنوع طابعتك في هذا الدرس استخدمنا طابعة HP LaserJet P1005 ثم انقر على Add Printer.

طابعة لاسلكية

هنا إعدادات الطباعة يمكنك تحريرها حسب ما هو مناسب لك أو تركها على الإعدادات الافتراضية وذلك بالنقر على Set Default option.

طابعة لاسلكية

إذا ظهرت لك هذه الصفحة فخطواتك صحيحة وإذا كان هناك مشكلة يمكنك الرجوع لخطوات السابقة.طابعة لاسلكية

تحميل حزمة HP وتهيئتها

حمّل الحزمة الخاصة بطابعة HP.

sudo apt install hplip hplip-data

هنا نقوم بتهيئة الحزمة.

sudo hp-setup -i

ادخل الرقم 0.raspberry-pi-print-server

ادخل حرف d.
التحكم بالطابعة عن بعد

انقر على Yes للموافقة على الترخيص.التحكم بالطابعة عن بعد

سيظهر اسم الطابعة انقر على m إذا كان صحيحًا.طابعة لاسلكية

الطباعة التجريبية

عد إلى المتصفح وانقر على Print Test Page.

raspberry-pi-print-server

ستبدأ الطابعة بطباعة ورقة تجريبية بهذا الشكل.
التحكم بالطابعة عن بعد

في خانة البحث اكتب الطابعات أو Printers انقر عليها وانقر على إضافة طابعة أو Add a Printer or scanner.التحكم بالطابعة عن بعد

الآن يمكنك التحكم بالطابعة عن بعد، ستظهر مباشرة قائمة الطابعات اللاسلكية الموجودة في المكان انقر على الطابعة التي قمت بإضافتها في الخطوات السابقة.التحكم بالطابعة عن بعد

 يمكنك ربطها مع أكثر من جهاز وطباعة الملفات لا سلكيًا.التحكم بالطابعة عن بعد

تأكد من قدرتك على التحكم بالطابعة عن بعد.

لا تنسَ فصل وحدة الطاقة بعد الانتهاء من استخدام الطابعة.




نظام أمان باستخدام الأردوينو ووحدة الاتصال اللاسلكي (SIM800l)

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

SMS و وحدة SIM800l

المواد والأدوات

اردوينو

X1 اردوينو 

SIM800l

X1 وحدة SIM800L

PIR SENSOR

X1 حساس الحركة

DC TO DC

X1 منظم جهد (DC TO DC)

Half-size Breadboard

X1 لوحة تجارب 

سلك يو اس بى A-B

X1سلك اردوينو 

اسلاك

 اسلاك توصيل(M/M) 

اسلاك

أسلاك توصيل (M/F)

 


وحدة SIM800L

 SIM800l

وحدة الاتصال اللاسلكي (SIM800L) تساعدك في مراقبة المنزل عن بعد أو تنشيط أي نظام داخل منزلك بمكالمة أو رسائل SMS عبارة عن مودم (GSM) مصغر فيمكن استخدامها لإجراء مهام ارسال أو استقبال الرسائل النصية القصيرة، إجراء مكالمات هاتفية أو استقبالها، الاتصال بالإنترنت من خلال (GPRS) و (TCP / IP)، يمكن استخدامها في عدد كبير من مشاريع إنترنت الأشياء. تحتاج أن تضيف شريحة 2G SIM في منفذ الشريحة لتتمكن من برمجته مع المتحكم حسب متطلبات مشروعك

مؤشر الوميض:

يوجد مؤشر الوميض أعلى الجانب الأيمن على وحدة الاتصال اللاسلكي (SIM800L) يشير إلى حالة شبكتك الخلوية. سيومض بمعدلات مختلفة لإظهار حالة الاتصال بالشبكة:

وميض كل ثانية :الوحدة قيد التشغيل ولكنها لم تتصل بالشبكة الخلوية.

SMS و وحدة SIM800l

وميض كل ثانيتين: تم تنشيط بيانات (GPRS)

SMS و وحدة SIM800l

وميض كل ثلاثة ثواني : اتصلت الوحدة بالشبكة الخلوية ويمكنها إرسال / استقبال الاتصال والرسائل القصيرة

منافذ وحدة الاتصال اللاسلكي (SIM800l)

SIM800l

الجدول التالي يوضح وظائف المنافذ

المنفذ في وحدة (SIM 800L) الوظيفة
NET منفذ للحام الهوائي الحلزوني المزود مع الوحدة .
VCC منفذ الطاقة للوحدة. من 3.4 فولت إلى 4.4 فولت. و 2أمبير عدم تزويد الوحدة بالطاقة بشكل كافي سيتسبب في عدم عملها بشكل صحيح
RST (Reset) RST (إعادة الضبط). سحب الدبوس LOW لمدة 100 مللي ثانية لإجراء إعادة تعيين ثابت.
RxD (Receiver) منفذ للاتصال التسلسلي
TxD (Transmitter) منفذ للاتصال التسلسلي
GND GND
RING دبوس مؤشر. يعطي اشارة HIGH افتراضيًا وسينبض LOW لمدة 120 مللي ثانية عند تلقي مكالمة. يمكن أيضًا تهيئته للنبض عند تلقي رسالة نصية قصيرة.
DTR لإلغاء وتنشيط وضع السكون حسب الاشارة التي يتم ارسالها له. إشارة HIGH تفعيل وضع السكون، وبالتالي تعطيل الاتصال التسلسلي. اشارة LOW تعطيل وضع السكون وتشغيل الوحدة.
MIC± مدخل المايكرفون
SPK± مدخل مكبر الصوت

توصيل الدائرة

قبل توصيل الدائرة عليك التأكد من أن خرج منظم الجهد (DC TO DC buck converter) يساوي 4.4 قد يتسبب زيادة الجهد إلى تلف وحدة الاتصال اللاسلكي (SIM800l)

SIM800l

البرمجة

برمجة هذا المشروع تحتاج إلى كتابة الشفرة البرمجية بلغة (C++)على بيئة التطوير المتكاملة للأردوينو Arduino IDE)) يمكن التعرف على البرنامج أكثر من خلال الرابط

أولا: عليك تحميل مكتبة (SoftwareSerial)

ثانيا: كتابة الأوامر البرمجية التالية

#include <SoftwareSerial.h>
SoftwareSerial mySerial(3, 2);
int p=7;
void setup()
{ 
pinMode(p, INPUT);
mySerial.begin(9600);
delay(1000);
}
void loop()
{int buttonState = digitalRead(7);
if (buttonState == HIGH) {
delay(1000);
mySerial.println("AT");
delay(500);
mySerial.println("AT+CMGF=1");
delay(500);
mySerial.println("AT+CMGS=\"+966XXXXXXXX\"\r");
delay(500);
mySerial.print("Warning! Motion detected!");
delay(500);
mySerial.write(26);}}

شرح الشفرة البرمجية (Code)

استدعاء مكتبة (SoftwareSerial)

 #include <SoftwareSerial.h>

تعريف منافذ الأردوينو التي تم توصيلها مع منافذ الارسال والاستقبال للاتصال التسلسلي (TX-RX) في وحدة الاتصال اللاسلكي (SIM800l) حيث تم توصيل المنفذ الرقمي 3 مع منفذ الاستقبال (RX) والمنفذ الرقمي 2 مع منفذ الارسال(TX)

 SoftwareSerial mySerial(3, 2);

تعريف منافذ الأردوينو الذي تم توصيل منفذ الخرج (Out) في حساس الحركة وهو المنفذ رقم 7

int p=7;

في دالة () void setup نُعرف منافذ الدخل والخرج

كذلك نقوم بتهيئة الاتصال التسلسلي

 
void setup()
{ 
pinMode(p, INPUT);
mySerial.begin(9600);
delay(1000);
}

في دالة void loop () سنعرف متغير يخزن داخله حالة حساس الحركة التي ستم قرأتها من المنفذ رقم 7
ثم نكتب الجملة الشرطية التي تجعل البرنامج ينتظر لمدة ثانية إذا كانت توجد حركة

 void loop()
{int buttonState = digitalRead(7);
if (buttonState == HIGH) {
delay(1000);

تحدد بروتوكولات ارتباط الاتصال في بداية الاتصال

mySerial.println("AT");
delay(500);

التشغيل على وضع نص SMS

mySerial.println("AT+CMGF=1");
delay(500);

تحديد الرقم الذي سيتم ارسال الرساله له

 
mySerial.println("AT+CMGS=\"+9665XXXXXX\"\r");
delay(500);

نص الرسالة الذي سيتم ارسالها في حال وجود حركة

 
mySerial.print("Warning! Motion detected!");
delay(500);
mySerial.write(26);}}