الكشف عن الأصوات من حولنا باستخدام الراسبيري باي

مقدمة

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

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

الكشف عن الأصوات

 1× راسبيري باي

الكشف عن الأصوات

 1× سلك (HDMI)

الكشف عن الأصوات

1× محول تيار (5V-2A)

الكشف عن الأصوات

 1× كرت ذاكرة

sound-sensor-raspberry-pi

1× سلك ايثرنتsound-sensor-raspberry-pi

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

sound detection sensor

حساس الصوت (Sound Detection Sensor)

تهيئة الراسبيري باي

أولا، سوف تحتاج إلى تثبيت نظام الراسبيان على الراسبيري باي إذا لم تكن قد فعلت ذلك قم بالإطلاع على  الدرس الأول نظام تشغيل الراسبيري باي

وبعد تثبيت النظام، يمكنك تهيئة النظام من خلال الرجوع  للدرس الثاني تهيئة نظام التشغيل

افتح الشاشة السوداء LXterminal للبدء باستخدام الراسبيري باي.

الكشف عن الأصوات

للبدء، قم بإستخدام الـ Termial  لتحديث الراسبيري باي إلى أحدث إصدار.

sudo apt-get update
sudo apt-get upgrade

ثم أعد تشغيل الراسبيري باي.

reboot

حمّل البايثون على الراسبيري باي.

sudo apt install python3 idle3

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

وصل الدائرة الكهربائية كما في الشكل التالي:sound-sensor-raspberry-pi

للمزيد حول حساس الكشف عن الأصوات يمكنك الرجوع للدرس التحكم بالـ LED  عن طريق التصفيق.

البرمجة

ستشاهد في قائمة Programming وجود برنامج جديد وهو Python 3 (IDLE) انقر عليه.

sound-sensor-raspberry-pi

 من قائمة File اختر  New File.

انسخ الكود التالي والصقه بالملف الفارغ.

import RPi.GPIO as GPIO
import time

#GPIO SETUP
channel = 17
GPIO.setmode(GPIO.BCM)
GPIO.setup(channel, GPIO.IN)

def callback(channel):
        if GPIO.input(channel):
                print ("Sound Detected!")
        else:
                print ("Sound Detected!")

GPIO.add_event_detect(channel, GPIO.BOTH, bouncetime=300)  # let us know when the pin goes HIGH or LOW
GPIO.add_event_callback(channel, callback)  # assign function to GPIO PIN, Run function on change

# infinite loop
while True:
        time.sleep(1)

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

في هذه الأسطر يتم استدعاء المكتبات الضرورية مثل RPi.GPIO و  time.

import RPi.GPIO as GPIO
import time

هنا سيتم تهيئة المنفذ 17 في الراسبيري باي لقراءة المدخلات.

#GPIO SETUP
channel = 17
GPIO.setmode(GPIO.BCM)
GPIO.setup(channel, GPIO.IN)

في حال تم التقاط صوت الحساس ستظهر رسالة Sound Detected.

def callback(channel):
        if GPIO.input(channel):
                print ("Sound Detected!")
        else:
                print ("Sound Detected!")

احفظ الملف من قائمة File اختر Save اكتب اسم الملف ثم انقر على Save.

sound-sensor-raspberry-pi

بعد ذلك افتح الشاشة السوداء LXterminal.

واكتب الأمر التالي لتنفيذ الكود البرمجي لمشروع الكشف عن الأصوات.

python DV.py

في حال تم قراءة أصوات من قبل الحساس ستظهر هذه الرسالة مباشرة.

sound-sensor-raspberry-pi

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

 




شاشة اللمس راسبيري باي 7 انش

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

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

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

راسبيري باي

X1 راسبيري باي 

محول طاقة

X1 محول طاقة

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

X1 شاشة اللمس راسبيري باي 7 انش

Ultimaker 2+ Ultimaker 3D printer: Price, Features, Videos…

طابعة ثلاثية الأبعاد (اختياري)

خيوط الطابعة ثلاثية الأبعاد

خيوط الطابعة (اختياري)

مفك براغي

1X مفك براغي 

براغي 3*10

X 4 براغي (3*10)

توصيل شاشة اللمس راسبيري باي7 انش

تتوصل الشريط الأبيض كما يظهر بالصورة و تثبيته

توصيل الأسلاك مع شاشة اللمس راسبيري باي

توصيل الأسلاك مع منافذ الشاشة كما يظهر بالجدول (مع مرعاة أن نبدأ بتوصيل الأسلاك من طرف الشاشة أولا ثم بعد تثبيت الراسبيري على الشاشة نوصلها من طرف الراسبيري باي)

الشاشة منافذ الراسبيري (GPIO board pin)
الطاقة (5v) 2
الأرضي 6
SDA 3
SCL 5

 

يمكن تحميل و من ثم طباعة حامل الشاشة من خلال الرابط

 

إضافة لوحة مفاتيح إفتراضية على نظام التشغل

 

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

sudo apt update 
sudo apt upgrade

لتثبيت برنامج لوحة المفاتيح الافتراضية ، من خلال  تشغيل الأمر التالي.

sudo apt install matchbox-keyboard

بعد ذلك يمكنك فتح لوحة المفاتيح من خلال النقر (علامة التوت <Accessories < Keyboard)

 

لوحة مفاتيح افتراضية مع شاشة اللمس راسبيري باي

إضافة لوحة المفاتيح الإفتراضية على شريط المهام

يمكنك إضافة رمز للوحة المفاتيح إلى شريط المهام. بحيث نتمكن من خلال النقر على الرمز من سيتم فتح وإغلاق لوحة المفاتيح على الشاشة تلقائيًا.

نبدأ بكتابة برنامج نصي في المجلد “/ usr / bin /” من خلال تشغيل الأمر التالي.

sudo nano /usr/bin/toggle-keyboard.sh

داخل هذا الملف ، أدخل سطور التعليمات البرمجية التالية.

#!/bin/bash
PID="$(pidof matchbox-keyboard)"
if [ "$PID" != "" ]; then
kill $PID
else
matchbox-keyboard &
fi

احفظ الملف بالضغط على CTRL + X ، ثم Y ، متبوعًا بـ ENTER.

نعطي إذن التنفيذ ، من خلال الأمر التالي.

sudo chmod +x /usr/bin/toggle-keyboard.sh

أنشئ ملف يساعد شريط المهام على قراءة وتحميل زر (رمز لوحة المفاتيح) من خلال الأمر التالي.

sudo nano /usr/share/raspi-ui-overrides/applications/toggle-keyboard.desktop

داخل هذا الملف ، أدخل الأسطر التالية.

[Desktop Entry]
Name=Toggle Virtual Keyboard
Comment=Toggle Virtual Keyboard
Exec=/usr/bin/toggle-keyboard.sh
Type=Application
Icon=matchbox-keyboard.png
Categories=Panel;Utility;MB
X-MB-INPUT-MECHANISM=True

احفظ الملف بالضغط على CTRL + X ، ثم Y ، متبوعًا بـ ENTER.

نحتاج إلى نسخ ملف التكوين الافتراضي إلى مجلد تهيئة مستخدمي pi.

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

cp /etc/xdg/lxpanel/LXDE-pi/panels/panel /home/pi/.config/lxpanel/LXDE-pi/panels/panel

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

nano /home/pi/.config/lxpanel/LXDE-pi/panels/panel

في آخر من هذا الملف ، أضف النص التالي.

Plugin {
type=launchbar
Config {
Button {
id=toggle-keyboard.desktop
}
}
}

هذا الجزء من النص يُنشئ مدخلا في شريط المهام. يخبر شريط المهام باستخدام ملف toggle-keyboard.desktop الذي أنشأناه سابقًا لعرض زر (لوحة المفاتيح ).

احفظ الملف بالضغط على CTRL + X ، ثم Y ، متبوعًا بـ ENTER.

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




اصنع راديو باستخدام اردوينو اونو ووحدة (TEA5767 FM)

في هذا الدرس ستتعرف على وحدة راديو (TEA5767 FM) و كيف تبرمجها مع اردوينو اونو، بحيث تتمكن من صنع جهاز راديو يحتوي على شاشة عرض تعرض قيمة تردد الإشارة، مع اثنين من ضغاط التحكم، يعمل الضغاط الأول على تغير قيمة التردد بزيادة 0.1 والثاني تغير القيمة بتنقيص 0.1. و يعمل الجهاز على التقاط اشارة محطات الراديو المتوفرة بمنطقتك ضمن نطاق تردد من 87.5 ميجاهرتز إلى 108 ميجاهرتز

 

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

 

اردوينو

X1 اردوينو اونو

سلك اردوينو

X1 سلك اردوينو

tea5767 fm radio

1X وحدة راديو (TEA5767 FM)

push buttons

X2 ضغاط التحكم 

مقاومة 220

X1 مقاومة 220 اوم

10 كيلو أوم

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

breadboard

1X لوحة تجارب

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

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

2*12 lcd

X1 شاشة كرستالية

40 رأس دبوس

X1 40 رأس دبوس

أسلاك توصيل

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

أسلاك توصيل

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

speaker 3.5 mm jack

1 X1 مكبر صوت بمنفذ 3.5 مللي متر

 

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

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

وحدة (TEA5767 FM)

وحدة راديو (TEA5767 FM) عبارة عن شريحة واحدة، تدعم بروتوكول الاتصال IC للتطبيقات منخفضة الجهد وتمثل راديو مصغر مضبوط رقميًا. يعمل في نطاقات الترددات الأوروبية والأمريكية واليابانية. تتكون الوحدة من منفذين 3.5 ملم، يستخدم الأول لتوصيل الهوائي، و زيادة مساحة التقاط الموجات والآخر منفذ السماعات.

tea5767 ports

كما تحتوي على 4 دبابيس:

VCC: وحدة إمداد الطاقة – 5 فولت
GND: الأرض
SLC: تزامن بروتوكول I2C يوصل مع المنفذ التناظري A5
SDA: بيانات بروتوكول I2C يوصل مع المنفذ التناظري A4

tea5767 pins

البرمجة

بالبداية ستحتاج إلى تحميل مكتبة (TEA5767 FM) و من ثم تثبيتها في بيئة تطوير الاردوينو (اردوينو IDE) يمكنك الاطلاع على الدرس الأول -تنصيب برنامج اردوينو  لمعرفة كيف يتم تثبيت المكتبات

 

#include<Wire.h>

#include <TEA5767Radio.h> 

#include <LiquidCrystal.h>

TEA5767Radio radio = TEA5767Radio();            
LiquidCrystal lcd(7, 6, 5, 4, 3, 2);              
float frequency = 0;                            
int fUP = A0;                                   
int fDOWN = A1;                                
int count = 0;                                   
void setup()
{
  delay(1000);
  Wire.begin();
  frequency = 91.1;                       //starting frequency
  radio.setFrequency(frequency);
  lcd.begin(16,2);
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("FM: ");
  lcd.setCursor(0, 1);
  lcd.print(frequency);
}void loop()
{
  
  if(digitalRead(fUP))                      
  {
    count=0;
    while (digitalRead(fUP))
    {
      count++;
      if(count > 0 && count <= 6)          
      {
        frequency += 0.1;
        frequencyUpdate();
        delay(200);
      }
      else if (count > 6 && count <= 2)   
      {
        frequency += 0.1;
        frequencyUpdate();
        delay(80);
      }
      else                                
      {
        frequency += 0.1;
        frequencyUpdate();
        delay(5);
      }
    }}
  
  
  
   if(digitalRead(fDOWN))                   
   {
     count = 0;
    while (digitalRead(fDOWN))            
    {
      count--;
      if(count < 0 && count >= -6)
      {
        frequency -= 0.1;
        frequencyUpdate();
        delay(200);
      }
      else if (count < -6 && count >= -12)
      {
        frequency -= 0.1;
        frequencyUpdate();
        delay(80);
      }
      else                                 
      {
        frequency -= 0.1;
        frequencyUpdate();
        delay(5);
      }
    }
   }  
  
}
void frequencyUpdate()                    
{
  frequency = constrain(frequency, 88.0, 108.0);
  lcd.setCursor(0,0);
  lcd.print("ESTACION DE FM:");
  lcd.setCursor(0,1);
  lcd.print(frequency);
  radio.setFrequency(frequency);
}

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

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

#include<Wire.h>
#include <TEA5767Radio.h> 
#include <LiquidCrystal.h>

نعرف كائن باسم (radio)

TEA5767Radio radio = TEA5767Radio();

نعرف منافذ الشاشة الكرستالية

LiquidCrystal lcd(7, 6, 5, 4, 3, 2);

نعرف متغيرات تشيل إلى التردد و منفذ ضغاط التحكم لزيادة و تنقيص قيمة التردد
يمثل ضغاط التحكم الموصل مع المنفذ A0 مفتاح التحكم بزيادة التردد بقيمة 0.1
و يمثل ضغاط التحكم الموصل مع المنفذ A1 مفتاح التحكم بتنقيص التردد بقيمة 0.1

float frequency = 0; 
int fUP = A0; 
int fDOWN = A1; 
int count = 0;

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

void setup()
{
delay(1000);
Wire.begin();
frequency = 91.1; //starting frequency
radio.setFrequency(frequency);
lcd.begin(16,2);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("FM: ");
lcd.setCursor(0, 1);
lcd.print(frequency);
}

في دالة ال(void loop) نبرمج دوال التحكم زيادة ونقصان قيمة التردد بناء على مدخلات مفاتيح التحكم

void loop()
{

if(digitalRead(fUP)) 
{
count=0;
while (digitalRead(fUP))
{
count++;
if(count > 0 && count <= 6) 
{
frequency += 0.1;
frequencyUpdate();
delay(200);
}
else if (count > 6 && count <= 2) 
{
frequency += 0.1;
frequencyUpdate();
delay(80);
}
else 
{
frequency += 0.1;
frequencyUpdate();
delay(5);
}
}}



if(digitalRead(fDOWN)) 
{
count = 0;
while (digitalRead(fDOWN)) 
{
count--;
if(count < 0 && count >= -6)
{
frequency -= 0.1;
frequencyUpdate();
delay(200);
}
else if (count < -6 && count >= -12)
{
frequency -= 0.1;
frequencyUpdate();
delay(80);
}
else 
{
frequency -= 0.1;
frequencyUpdate();
delay(5);
}
}
} 

}

في دالة (frequencyUpdate) يتم تحديث قيم التررد بناء على المدخلات من المفاتيح

void frequencyUpdate() 
{
frequency = constrain(frequency, 88.0, 108.0);
lcd.setCursor(0,0);
lcd.print("STACION FM:");
lcd.setCursor(0,1);
lcd.print(frequency);
radio.setFrequency(frequency);
}

 




اختبر مهاراتك في الرياضيات مع الاردوينو

مقدمة

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

diy-arduino-lcd-keypad-maths

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

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

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

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

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

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

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

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

1× 40 رأس دبوس

تحديد الاتجاهات

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

تحديد الاتجاهات

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

diy-arduino-lcd-keypad-maths

1× 2C / IIC Serial Interface Module

diy-arduino-lcd-keypad-maths

لوحة مفاتيح

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

لمعرفة المزيد حول الشاشة الكرستالية يمكنك الرجوع للدرس التحكم بالشاشة الكرستالية LCD.

للمزيد حول لوحة المفاتيح يمكنك الرجوع للدرس لوحة المفاتيح Keypad.

لابد من تلحيم المنافذ مع الشاشة الكرستالية، للمزيد حول اللحام يمكنك الرجوع للدرس تعلم كيفية التلحيم – تلحيم القطع باللوحة الإلكترونيةdiy-arduino-lcd-keypad-maths

البرمجة

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

#include <Keypad.h>
#include <LiquidCrystal_I2C.h> //LCD library
LiquidCrystal_I2C lcd(0x27, 16, 2);

byte sad[8] = 
{
B00000,
B01010,
B01010,
B00000,
B00000,
B01110,
B10001,
};


unsigned long time=0;
unsigned long inicio=0;
int m, mu=0,md=0; 
int s, su=0,sd=0;
int c,cu,cd=0;
byte intento=0;


char level;
boolean modePlay=false;
int cifra_azar;
int numero1=0;
int numero2=0;
int numero3=0;
int temp=0;
int resultado;
String operando="";
String sResultado;
String sLevel;
int largo=0;
boolean activar=false;
boolean temporizar = false;

char cifra_jugador[4]; //Stores the number of the player

String numero_jugador = String(); //Almacena las 4 cifras del jugador
String sNumero_jugador;

int cuenta=0;
int i,j,x;
int puntos,famas=0;
int intentos=0;
int maximo_intentos=10;


const byte ROWS=4;
const byte COLS=4;
char keys[ROWS][COLS] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
byte rowPins[ROWS] = {13,6,5,4}; //Filas(pines del 9 al 6)
byte colPins[COLS] = {3,2,1,0}; //Columnas (pines del 5 al 2)
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );


void game_over()
{
temporizar=false;
modePlay=false;
lcd.clear();
lcd.setCursor(4,0);
lcd.print("Game Over");
lcd.setCursor(0,1);
lcd.print("Time: ");
lcd.print(md);
lcd.print(mu);
lcd.print(":");
lcd.print(sd);
lcd.print(su); 
lcd.print(":");
lcd.print(cd);
lcd.print(cu);
}

void generate_random()
{
lcd.clear();
cuenta=0;
sResultado="";
sNumero_jugador="";

randomSeed(analogRead(analogRead(0)));

switch(level)
{
case '1':

numero1=random(1,11); //Generates a number between un número aleatorio entre 1 and 10
numero2=random(1,11); //Generates a number between un número aleatorio entre 1 and 10 
break;

case '2':

numero1=random(50,100); //Generates a number between un número aleatorio entre 50 and 99
numero2=random(1,11); //Generates a number between un número aleatorio entre 1 and 10
break;

case '3':

numero1=random(50,100); //Generates a number between un número aleatorio entre 1 y 99
numero2=random(50,100); //Generates a number between un número aleatorio entre 1 y 10 
break;

} 

numero3=random(1,5); //Generates a number between 1 and 4

switch(numero3)
{
case 1:
operando="+";
resultado=numero1+numero2;
break;
case 2:
operando="-";
if(numero1<numero2)
{
temp=numero1;
numero1=numero2;
numero2=temp;
}
resultado=numero1-numero2; 

break;
case 3:
operando="*";
resultado=numero1*numero2; 
break;
case 4:
operando="/";
resultado=numero1%numero2;
if(resultado!=0) //If not is zero the mod
{
operando="*";
resultado=numero1*numero2; 
}
else //The mod is zero
{
resultado=numero1/numero2;
}
}

sResultado= String(resultado);

lcd.setCursor(0,0);
lcd.print(numero1);
lcd.setCursor(2,0);
lcd.print(operando);
lcd.setCursor(3,0);
lcd.print(numero2); 
lcd.setCursor(12,1);
lcd.print(cuenta);

lcd.setCursor(0,1); 
lcd.print(" ");
lcd.setCursor(0,1);

}

void timer()
{

if(modePlay==true)
{
time = millis()-inicio; 

m=(time/1000)/60; //Minutes
mu=m%10; 
md=(m-mu)/10; 

s=(time/1000)%60; //Seconds
su=s%10; 
sd=(s-su)/10;

c=(time/100)%60;
cu=c%10;
cd=(c-cu)/10;

lcd.setCursor(8,0);
lcd.print(md);
lcd.print(mu);
lcd.print(":");
lcd.print(sd);
lcd.print(su); 
lcd.print(":");
lcd.print(cd);
lcd.print(cu); 


}

}


void setup() 
{
// put your setup code here, to run once:
lcd.createChar(1,sad); 
lcd.begin(); 
choose(); //Displays the select level mode

}

//********************************************************
void verificar()
{
if(sNumero_jugador==sResultado)
{ 

lcd.setCursor(6,0);
lcd.print("G");

intento=intento+1; 

generate_random();

}

else

{


lcd.setCursor(0,1); 

cuenta=0;

sNumero_jugador="";

lcd.setCursor(6,1);
lcd.write(byte(1)); //this writes the sad face



}


lcd.setCursor(8,1);
lcd.print("Try:");
lcd.print(intento);
lcd.print("/10");

if(intento==maximo_intentos)
{
game_over(); // Ends the game
}

}


void choose()
{
modePlay=false;
intento=0;
lcd.clear();
lcd.setCursor(2,0);
lcd.print("Select level");
lcd.setCursor(0,1);
lcd.print("1-E 2-M 3-H");

}

void conteo()
{
lcd.clear();

lcd.setCursor(4,0);

lcd.print(sLevel);

delay(300);

lcd.clear();

for(int x=3;x>=1;x--)
{
lcd.setCursor(8,0);

lcd.print(x);

delay(300);


}

lcd.clear();

lcd.setCursor(7,0);

lcd.print("Go");

delay(600);


generate_random();

modePlay=true;

if(activar==false)
{
inicio=millis();
activar=true;
}
}


void loop()
{
timer();

// put your main code here, to run repeatedly:

char key = keypad.getKey();

if(key)

{ 
//If is the select level display
if(modePlay==false) 
{
if(key=='1' || key=='2' || key=='3')
{
level=key;

lcd.clear();

lcd.setCursor(5,1);

switch(level)
{
case '1':
sLevel="Easy Level"; 
break;
case '2':
sLevel="Medium Level";
break;
case '3':
sLevel="Hard Level";
break;

} //end switch

conteo();

} // end IF key select level

} //end IF mode play off

else
//Mode player

{

temporizar=true;

if(activar==false)
{
inicio=millis();
activar=true;
lcd.clear();
} 


if(key!='A' && key!='B' && key!='C' && key!='D')
{

cifra_jugador[cuenta] = key;

numero_jugador = String(key);

sNumero_jugador=sNumero_jugador+numero_jugador;

lcd.setCursor(0+cuenta,1);

lcd.print(cifra_jugador[cuenta]);

cuenta++; 

largo=sResultado.length();

if(cuenta==largo)
{
// lcd.setCursor(10,1);
verificar();
}


} //End if key!= 


} // End else mode player


if(key=='A')
{ 
modePlay=false;
inicio=millis();
activar=false; 
choose();
}

} //End if Key main

} //End loop

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

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

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

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

Sketch > Include libraries > Add ZIP library 

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

#include <LiquidCrystal_I2C.h>

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

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

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

Sketch > Include libraries > Add ZIP library 

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

#include <Keypad.h>

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

LiquidCrystal_I2C lcd(0x27, 16, 2);

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

byte sad[8] = 
{
B00000,
B01010,
B01010,
B00000,
B00000,
B01110,
B10001,
};

نقوم بتهيئة المتغيرات التي سنحتاجها بالمشروع.

unsigned long time=0;
unsigned long inicio=0;
int m, mu=0,md=0; 
int s, su=0,sd=0;
int c,cu,cd=0;
byte intento=0;


char level;
boolean modePlay=false;
int cifra_azar;
int numero1=0;
int numero2=0;
int numero3=0;
int temp=0;
int resultado;
String operando="";
String sResultado;
String sLevel;
int largo=0;
boolean activar=false;
boolean temporizar = false;

char cifra_jugador[4]; //Stores the number of the player

String numero_jugador = String(); //Almacena las 4 cifras del jugador
String sNumero_jugador;

int cuenta=0;
int i,j,x;
int puntos,famas=0;
int intentos=0;
int maximo_intentos=10;


const byte ROWS=4;
const byte COLS=4;
char keys[ROWS][COLS] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
byte rowPins[ROWS] = {13,6,5,4}; //Filas(pines del 9 al 6)
byte colPins[COLS] = {3,2,1,0}; //Columnas (pines del 5 al 2)
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

نهيئ دالة game_over والتي تخبرنا بانتهاء اللعبة وظهور الوقت المستغرق لإتمام اللعبة من قبل اللاعب.

void game_over()
{
temporizar=false;
modePlay=false;
lcd.clear();
lcd.setCursor(4,0);
lcd.print("Game Over");
lcd.setCursor(0,1);
lcd.print("Time: ");
lcd.print(md);
lcd.print(mu);
lcd.print(":");
lcd.print(sd);
lcd.print(su); 
lcd.print(":");
lcd.print(cd);
lcd.print(cu);
}

نهيئ دالة generate_random والتي تقوم بطباعة الدوال والأرقام بشكل تلقائي.

void generate_random()
{
lcd.clear();
cuenta=0;
sResultado="";
sNumero_jugador="";

randomSeed(analogRead(analogRead(0)));

switch(level)
{
case '1':

numero1=random(1,11); //Generates a number between un número aleatorio entre 1 and 10
numero2=random(1,11); //Generates a number between un número aleatorio entre 1 and 10 
break;

case '2':

numero1=random(50,100); //Generates a number between un número aleatorio entre 50 and 99
numero2=random(1,11); //Generates a number between un número aleatorio entre 1 and 10
break;

case '3':

numero1=random(50,100); //Generates a number between un número aleatorio entre 1 y 99
numero2=random(50,100); //Generates a number between un número aleatorio entre 1 y 10 
break;

} 

numero3=random(1,5); //Generates a number between 1 and 4

switch(numero3)
{
case 1:
operando="+";
resultado=numero1+numero2;
break;
case 2:
operando="-";
if(numero1<numero2)
{
temp=numero1;
numero1=numero2;
numero2=temp;
}
resultado=numero1-numero2; 

break;
case 3:
operando="*";
resultado=numero1*numero2; 
break;
case 4:
operando="/";
resultado=numero1%numero2;
if(resultado!=0) //If not is zero the mod
{
operando="*";
resultado=numero1*numero2; 
}
else //The mod is zero
{
resultado=numero1/numero2;
}
}

sResultado= String(resultado);

lcd.setCursor(0,0);
lcd.print(numero1);
lcd.setCursor(2,0);
lcd.print(operando);
lcd.setCursor(3,0);
lcd.print(numero2); 
lcd.setCursor(12,1);
lcd.print(cuenta);

lcd.setCursor(0,1); 
lcd.print(" ");
lcd.setCursor(0,1);

}

في دالة timer نهيئ المؤقت لبدء حساب الوقت أثناء اللعب.

void timer()
{

if(modePlay==true)
{
time = millis()-inicio; 

m=(time/1000)/60; //Minutes
mu=m%10; 
md=(m-mu)/10; 

s=(time/1000)%60; //Seconds
su=s%10; 
sd=(s-su)/10;

c=(time/100)%60;
cu=c%10;
cd=(c-cu)/10;

lcd.setCursor(8,0);
lcd.print(md);
lcd.print(mu);
lcd.print(":");
lcd.print(sd);
lcd.print(su); 
lcd.print(":");
lcd.print(cd);
lcd.print(cu); 


}

}

في الدالة ()setup نقوم بتهيئة الشاشة الكرستالية استعدادًا لطباعة العمليات الحسابية عليها.

void setup() 
{
// put your setup code here, to run once:
lcd.createChar(1,sad); 
lcd.begin(); 
choose(); //Displays the select level mode

}

في الدالة choose يتم الطباعة على الشاشة بمستويات اللعبة تبدأ من المستوى 1 وحتى المستوى 3.

void choose()
{
modePlay=false;
intento=0;
lcd.clear();
lcd.setCursor(2,0);
lcd.print("Select level");
lcd.setCursor(0,1);
lcd.print("1-E 2-M 3-H");

}

في الدالة main يتم مناداة الدوال السابقة استعدادًا لطباعة مدلولاتها على الشاشة وأخذ القيم من لوحة المفاتيح.

void loop()
{
timer();

// put your main code here, to run repeatedly:

char key = keypad.getKey();

if(key)

{ 
//If is the select level display
if(modePlay==false) 
{
if(key=='1' || key=='2' || key=='3')
{
level=key;

lcd.clear();

lcd.setCursor(5,1);

switch(level)
{
case '1':
sLevel="Easy Level"; 
break;
case '2':
sLevel="Medium Level";
break;
case '3':
sLevel="Hard Level";
break;

} //end switch

conteo();

} // end IF key select level

} //end IF mode play off

else
//Mode player

{

temporizar=true;

if(activar==false)
{
inicio=millis();
activar=true;
lcd.clear();
} 


if(key!='A' && key!='B' && key!='C' && key!='D')
{

cifra_jugador[cuenta] = key;

numero_jugador = String(key);

sNumero_jugador=sNumero_jugador+numero_jugador;

lcd.setCursor(0+cuenta,1);

lcd.print(cifra_jugador[cuenta]);

cuenta++; 

largo=sResultado.length();

if(cuenta==largo)
{
// lcd.setCursor(10,1);
verificar();
}


} //End if key!= 


} // End else mode player


if(key=='A')
{ 
modePlay=false;
inicio=millis();
activar=false; 
choose();
}

} //End if Key main

} //End loop

بعد اكتمال عملية الرفع يمكنك أن تختبر مهاراتك في الرياضيات.

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




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

مقدمة

في هذا الدرس سنستخدم لوحة الاردوينو وعصا التحكم والشاشة الكرستالية؛ لتتعرف على برمجة عصا التحكم وطباعة الاتجاهات على المحور. (x,y) على الشاشة الكرستالية
show-directions-using-arduino-joystick

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

اردوينو

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

سلك اردوينو

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

show-directions-using-arduino-joystick

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

دولار نينجا

1× 40 رأس دبوس

أسلاك توصيل

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

أسلاك توصيل

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

تحديد الاتجاهات

1× 2C / IIC Serial Interface Module

1×  عصا التحكم

show-directions-using-arduino-joystick

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

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

لمعرفة المزيد حول الشاشة الكرستالية يمكنك الرجوع للدرس التحكم بالشاشة الكرستالية LCD.

لابد من تلحيم المنافذ مع الشاشة الكرستالية، للمزيد حول اللحام يمكنك الرجوع للدرس تعلم كيفية التلحيم – تلحيم القطع باللوحة الإلكترونيةshow-directions-using-arduino-joystick

البرمجة

ارفع الكود البرمجي الخاص بمشروع تحديد الاتجاهات على لوحة الاردوينو باستخدام بيئة تطوير الاردوينو IDE.

#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);

void setup() 
{
analogReference(DEFAULT);
pinMode(A0, INPUT_PULLUP);
pinMode(A1, INPUT);
pinMode(A2, INPUT);
lcd.begin(); 
}

void loop() {
int X, Y, P ;
Y = analogRead(A1); 
X = analogRead(A2); 
P = digitalRead(A0); 
lcd.setCursor(0, 0);
if ((480 <= X) & (530 >= X) & (480 <= Y) & (530 >= Y)) {
}

if ((531 <= X) & (480 <= Y) & (530 >= Y)) {
lcd.print("UP");
}

if ((X <= 479) & (480 <= Y) & (530 >= Y)) {
lcd.print("Down");
}

if ((480 <= X) & (530 >= X) & (531 <= Y)) {
lcd.print("Right");
}

if ((480 <= X) & (530 >= X) & (Y <= 479)) {
lcd.print("Left");
}

if ((531 <= X) & (531 <= Y)) {
lcd.print("UP and Right");
}

if ((531 <= X) & (Y <= 479)) {
lcd.print("UP and Left");
}

if ((X <= 479) & (531 <= Y)) {
lcd.print("Down and Right");
}

if ((X <= 479) & (Y <= 479)) {
lcd.print("Down and Left");
}

if (P == 0) {
lcd.setCursor(0, 1);
lcd.print("PUSH");
}

delay(300);
lcd.clear();
}

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

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

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

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

Sketch > Include libraries > Add ZIP library .

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

#include <LiquidCrystal_I2C.h>

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

LiquidCrystal_I2C lcd(0x27, 16, 2);

في الدالة ()setup سنعرف متغيرات عصا التحكم والتي ستكون كمدخلات.

وسنقوم بتهيئة الشاشة الكرستالية استعدادًا لطباعة المخرجات.

void setup() 
{
analogReference(DEFAULT);
pinMode(A0, INPUT_PULLUP);
pinMode(A1, INPUT);
pinMode(A2, INPUT);
lcd.begin(); 
}

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

الجهات التي سيتم قراءتها وطباعتها على الشاشة:

(الأعلى- الأسفل-يمين-يسار- أعلى اليسار-أعلى اليمين-أسفل اليسار-أسفل اليمين).

void loop() {
int X, Y, P ;
Y = analogRead(A1); 
X = analogRead(A2); 
P = digitalRead(A0); 
lcd.setCursor(0, 0);
if ((480 <= X) & (530 >= X) & (480 <= Y) & (530 >= Y)) {
}

if ((531 <= X) & (480 <= Y) & (530 >= Y)) {
lcd.print("UP");
}

if ((X <= 479) & (480 <= Y) & (530 >= Y)) {
lcd.print("Down");
}

if ((480 <= X) & (530 >= X) & (531 <= Y)) {
lcd.print("Right");
}

if ((480 <= X) & (530 >= X) & (Y <= 479)) {
lcd.print("Left");
}

if ((531 <= X) & (531 <= Y)) {
lcd.print("UP and Right");
}

if ((531 <= X) & (Y <= 479)) {
lcd.print("UP and Left");
}

if ((X <= 479) & (531 <= Y)) {
lcd.print("Down and Right");
}

if ((X <= 479) & (Y <= 479)) {
lcd.print("Down and Left");
}

if (P == 0) {
lcd.setCursor(0, 1);
lcd.print("PUSH");
}

delay(300);
lcd.clear();
}

بعد اكتمال عملية الرفع يمكنك اختبار نظام تحديد الاتجاهات.

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




صنع جهاز للتحكم عن بعد بالروبوت باستخدام (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
}

 




اتصال لاسلكي بين لوحتين اردوينو باستخدام (RF 433Mhz)

المقدمة

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

RF-COMMUNICATION اتصال

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

اردوينو

X2 اردوينو اونو

سلك اردوينو

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

محول طاقة

X2 محول طاقة

RF مرسل و مستقبل

X1 المستقبل والمرسل لموجات الراديو الترددي (RF 433Mhz)

لوحة تجارب

X2 لوحة تجارب

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

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

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

X1 الثنائي المشع للضوء (LED)

مقاومة 220

X1 مقاومة 220 أوم

أسلاك توصيل

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

المستقبل والمرسل لموجات الراديو الترددي (RF 433Mhz)

يمكن ارسال البيانات بين لوحتين اردوينو باستخدام وحدة المستقبل والمرسل لموجات الراديو الترددي (RF 433Mhz). حيث ترسل البيانات بشكل متسلسل من جهاز المرسل إلى جهاز المستقبل.
تعمل وحدات المرسل لموجات الراديو الترددي المستخدمة بالدرس بتردد محدد يبلغ 433 ميجاهرتز. تُستخدم هذه الوحدات في الاتصالات قصيرة المدى. ويتم استخدامه في مجالات مختلفة مثل أجهزة التحكم في الإضاءة عن بُعد، وأنظمة RFID، والإنذار اللاسلكي وأنظمة الأمان وما إلى ذلك.
تعمل وحدة المرسل لموجات الراديو الترددي على اصدار موجات بشكل مستمر وتؤثر اشارة البيانات على سعة الموجة ما يعرف بتقنية تضمين إزاحة السعة (Amplitude Shift Keying). عندما تكون إشارة البيانات (1)، يتم تشغيل إشارة الناقل وعندما تكون إشارة البيانات (0) يتم إيقاف تشغيل إشارة الناقل.

موجات RF

وتحتوي وحدة المستقبل على دارة RF لتضخيم الموجة الحاملة المستقبلة من جهاز الإرسال.

 

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

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

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

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

البرمجة

بالبداية ستحتاج إلى تحميل مكتبة (VirtualWire) و من ثم تثبيتها على بيئة تطوير الاردوينو (Arduino IDE) يمكنك الاطلاع على الدرس الأول -تنصيب برنامج اردوينو (IDE) على جهازك من سلسة تعلم الاردوينو لمعرفة كيف يتم تثبيت المكتبات

برمجة المرسل 

#include <VirtualWire.h> 
#define size 1
int pot = A0;
byte TX_buffer[size]={0};
byte i;


void setup() 
{

vw_set_tx_pin(3); // pin
vw_setup(2000); // bps
for(i=0;i<size;i++)
{
TX_buffer[i]=i;
}
}

void loop()
{ 
int val = map(analogRead(pot),0,1024,0,255);
TX_buffer[0] = val; 
vw_send(TX_buffer, size); 
vw_wait_tx(); 
delay(10); 
}

برمجة المستقبل 

 

#include <VirtualWire.h> 
byte message[VW_MAX_MESSAGE_LEN]; // a buffer to store the incoming messages
byte messageLength = VW_MAX_MESSAGE_LEN; // the size of the message
int received_number = 0;
int LED = 5;

void setup()
{
Serial.begin(9600);
pinMode(LED, OUTPUT);
Serial.println("Ready...");
vw_set_rx_pin(3); // pin
vw_setup(2000); // bps
vw_rx_start();
}

void loop()
{
if (vw_get_message(message, &messageLength)) // non-blocking
{
Serial.print("Potentiometer: ");
for (int i = 0; i < messageLength; i++)
{
//Serial.print(message[i]);
received_number = message[i];
}
Serial.println(received_number);
analogWrite(LED, received_number);
}
}

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

أولا المرسل

في البداية سنقوم باستدعاء مكتبة (VirtualWire) والتي تتضمن دوال تساعدنا على برمجة الوحدة، ونعرف منفذ المقاومة المتغيرة

#include <VirtualWire.h> 
#define size 1
int pot = A0;
byte TX_buffer[size]={0};
byte i;

في دالة void setup()  نهيئ منفذ الارسال

void setup() 
{

vw_set_tx_pin(3); // pin
vw_setup(2000); // bps
for(i=0;i<size;i++)
{
TX_buffer[i]=i;
}
}

في دالة void loop() نأخذ قراءة من المقاومة المتغيرة ضمن مدى من 0 إلى 255 والتي  تمثل شدة الإضاءة المطلوبة، و نرسل الإشارة

void loop()
{ 
int val = map(analogRead(pot),0,1024,0,255);
TX_buffer[0] = val; 
vw_send(TX_buffer, size); 
vw_wait_tx(); 
delay(10); 
}

ثانيا المستقبل

نقوم باستدعاء مكتبة VirtualWire وتعريف الإشارة المستقبلة وحجم الرسالة، وتعريف منفذ الثنائي المشع للضوء (LED)

#include <VirtualWire.h> 
byte message[VW_MAX_MESSAGE_LEN]; 
byte messageLength = VW_MAX_MESSAGE_LEN; 
int received_number = 0;
int LED = 5;

في دالة void setup() نقوم بتهيئة منفذ الاتصال التسلسلي حتى نتمكن من طباعة القيم المستقبلة على شاشة الاتصال  وكمت سيتم تعريف منافذ الخرج

void setup()
{
Serial.begin(9600);
pinMode(LED, OUTPUT);
Serial.println("Ready...");
vw_set_rx_pin(3); // pin
vw_setup(2000); // bps
vw_rx_start();
}

في دالة void loop() نستقبل الرسالة و نعكس قيمتها ليتم تحديد شدة اضاءة الثنائي المشع للضوء (LED)

void loop()
{
if (vw_get_message(message, &messageLength)) // non-blocking
{
Serial.print("Potentiometer: ");
for (int i = 0; i < messageLength; i++)
{
//Serial.print(message[i]);
received_number = message[i];
}
Serial.println(received_number);
analogWrite(LED, received_number);
}
}

 

 

 




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

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

 

voltmeter

 

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

اردوينو

X1 اردوينو اونو

DC-Voltage-Sensor

X1 مستشعر جهد التيار المستمر

LCD

X1 الشاشة الكرستالية

X1لوحة تجارب

arduino-usb-lead-blue

X1 سلك اردوينو

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

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

أسلاك توصيل

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

us-sanitizer-without-touch

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

 

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

Arduino-Uno- voltage sensor

البرمجة

 

#include "LiquidCrystal.h"

const int voltageSensor = A0;

float vOUT = 0.0;
float vIN = 0.0;
float R1 = 30000.0;
float R2 = 7500.0;
int value = 0;

LiquidCrystal lcd(7, 6, 5, 4, 3, 2); // RS, E, D4, D5, D6, D7

void setup()
{
//Serial.begin(9600);
lcd.begin(16,2);
lcd.print(" Measure > 25V ");
delay(2000);
}

void loop()
{
value = analogRead(voltageSensor);
vOUT = (value * 5.0) / 1024.0;
vIN = vOUT / (R2/(R1+R2));
//Serial.print("Input = ");
//Serial.println(vIN);
lcd.setCursor(0,0);
lcd.print("Input = ");
lcd.setCursor(9,0);
lcd.print(vIN);
delay(500);
}

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

بالبداية يتطلب استدعاء مكتبة الشاشة الكرستالية

#include "LiquidCrystal.h"

نعرف منفذ لقراءة مستشعر جهد التيار المستمر

const int voltageSensor = A0;

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

float vOUT = 0.0;
float vIN = 0.0;

نعرف قيمة المقاومات الموجودة بالحساس

float R1 = 30000.0;
float R2 = 7500.0;
int value = 0;

نعرف منافذ الشاشة الكرستالية

LiquidCrystal lcd(7, 6, 5, 4, 3, 2); // RS, E, D4, D5, D6, D7

في دالة void setup() نكتب أوامر تهيئة الشاشة

void setup()
{

lcd.begin(16,2);
lcd.print(" Measure > 25V ");
delay(2000);
}

في دالة void loop() نأخذ القراءات من المستشعر و نكتب المعادلات لحساب قيمة الجهد
ليتم طباعتها على الشاشة الكرستالية

void loop()
{
value = analogRead(voltageSensor);
vOUT = (value * 5.0) / 1024.0;
vIN = vOUT / (R2/(R1+R2));
//Serial.print("Input = ");
//Serial.println(vIN);
lcd.setCursor(0,0);
lcd.print("Input = ");
lcd.setCursor(9,0);
lcd.print(vIN);
delay(500);
}



لعبة دولار النينجا (Ninja Dollar) باستخدام الاردوينو

مقدمة

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

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

دولار نينجا

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

ninja-dollar-lcd-game

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

دولار نينجا

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

دولار نينجا

40 رأس دبوس

ninja-dollar-lcd-game

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

دولار نينجا

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

دولار النينجا

1× 2C / IIC Serial Interface Module

شفرة مورس

ضغاط تحكم
دولار نينجا

2× مقاومة 1K Ω

اردوينو

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

دولار نينجا

 مصدر صوت

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

لمعرفة المزيد حول الشاشة الكرستالية يمكنك الرجوع للدرس التحكم بالشاشة الكرستالية LCD.

لابد من تلحيم المنافذ مع الشاشة الكرستالية، للمزيد حول اللحام يمكنك الرجوع للدرس تعلم كيفية التلحيم – تلحيم القطع باللوحة الإلكترونية

دولار النينجا

البرمجة

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

#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
const int buttonPin1=1;
const int buttonPin2=6;
const int buzzer=7;
unsigned long pts=0;
//set buttonstates
bool buttonState1=0;
bool buttonState2=0;
//random number for position of obstacles
int randomNums[6];
//random number for number of obstacles
int randomNum=0;
//random number for position of pts
int randomNums1[3];
//random number for number of pts
int randomNum1=0;
//start delay time, which decreases gradually
unsigned int myDelay=500;
//made this boolean to check if button2 is pressed because if it's pressed once in the first for loop i want obstacles not to be written until the end of it
bool temp=0;
//this variable stores the positions of the warrior while he shoots. there can be 16 positions because the warrior has 16 positions.
int tempI[16];
//i use this boolean to check if the point is catched
bool temp1=0;

//use this variable to store the position of the catched point. it must be and array to store the position of all the points in one iteration of the first for loop. if it stored just one position, then the "old" point would come back in the new iteration
int tempI1[3]; 
//use this variable to have a number of shots of the warrior, which is also the length of an array tempI
int button2IsPressed=0;

void setup() {
  // set up the LCD's number of columns and rows:
  lcd.begin();
  // set buttonpin mode
  pinMode(buttonPin1,INPUT);
  pinMode(buttonPin2,INPUT);
  pinMode(buzzer,OUTPUT);
  lcd.setCursor(4,0);
  lcd.print("THE GAME");
  lcd.setCursor(2,1);
  lcd.print("STARTS IN ");
  lcd.print("5");
  delay(1000);
  lcd.setCursor(12,1);
  lcd.print("4");
  delay(1000);
  lcd.setCursor(12,1);
  lcd.print("3");
  delay(1000);
  lcd.setCursor(12,1);
  lcd.print("2");
  delay(1000);
  lcd.setCursor(12,1);
  lcd.print("1");
  delay(1000);
  lcd.clear();

}

void loop() {
  here:
 

  randomNum=random(5);
  for(int i=0; i<randomNum; ++i){
  randomNums[i]=random(16);}
   randomNum1=random(3);
  for(int i=0; i<randomNum1; ++i){
  randomNums1[i]=random(16);
  }
  //i reset temp variable before every new for loop because the cycle of the moving warrior starts again
  temp=0;
  for(int j=0; j<3; ++j){
  tempI1[j]=0;
  }
  button2IsPressed=0;
//loop that writes dollar(warrior) on the lcd which jumps everytime button is pressed
    for(int i=0; i<16; ++i){
          
          
          //pts must be written here because of the lcd.clear() below (pts wouldn't be written down the whole time otherwise)
          //i must check if pts is greater than 9 or 99 etc to know how many gaps i should  leave for the number. the more points are collected, the smaller delay time is and the faster is dollar
          if(pts>9 && pts<20) 
          {lcd.setCursor(14,0);
           myDelay=400;
          }
          else if(pts>19 && pts<30) {
            lcd.setCursor(14,0);
            myDelay=300;
          }
          else if(pts>29 && pts<50) 
          {
            myDelay=200;
            lcd.setCursor(14,0);
          }
          
          else if(pts>=50){
            pts=0;
            myDelay=500;
            lcd.clear();
            lcd.setCursor(5,0);
            lcd.print("VICTORY");
            tone(buzzer,262);
            delay(200);
            tone(buzzer,330);
            delay(200);
            tone(buzzer,392);
            delay(100);
            tone(buzzer,330);
            delay(100);
            tone(buzzer,392);
            delay(100);
            tone(buzzer,523);
            delay(200);
            noTone(buzzer);
            delay(3000);
            lcd.clear();
            goto here;
            }
          else lcd.setCursor(15,0);
          lcd.print(pts);
            
         buttonState1=digitalRead(buttonPin1);
         buttonState2=digitalRead(buttonPin2);
 //setting obstacles if the warrior didn't shoot or he shot but he also jumped
 if(!temp){
          for(int j=0; j<randomNum; ++j){
          lcd.setCursor(randomNums[j],1);
          lcd.print("#");
       }
 }
 //checking if the warrior  had shot but there were obstacles before him, we want those obstacles to stay 
 else{
  for(int j=0; j<randomNum; ++j){
 //i check only the tempI[0] position because that's the when the warrior shot for the first time and he removed all the remaining obstacles 
    if(tempI[0]>randomNums[j]){
      lcd.setCursor(randomNums[j],1);
      lcd.print("#");
    
      }

      }
  }        
          

//setting pts
if(!temp1){
          for(int j=0; j<randomNum1; ++j){
          lcd.setCursor(randomNums1[j],0);
          
          lcd.print("*");
           
          }
} 
else{
  for(int j=0; j<randomNum1; ++j){
  
   if(randomNums1[j]!=tempI1[j]){
    lcd.setCursor(randomNums1[j],0);
    lcd.print("*");
    }
  }
  }
           
          
        
          //if the button is pressed we set the cursor up (so that our warrior jumps)
          if(buttonState1==HIGH) 
          {lcd.setCursor(i,0);
          tone(buzzer,131);
          delay(200);
          noTone(buzzer);
          }
          else lcd.setCursor(i,1);
          
          lcd.print("$");
         
          //if button2 is pressed our warrior shoots. i had to put this loop here so that warrior shoots from his current spot and then continues to move. that's why delay time is 5 so that this loop finishes as soon as possible
           if (buttonState2==HIGH) 
         {
          tone(buzzer,175);
         delay(100);
         noTone(buzzer);
         
          temp=1;
          //if the button1 is low then we remove all the obstacles. that's why the state if temp variable is changed
          if(buttonState1==LOW) 
          {
            
            tempI[button2IsPressed]=i;
          }
          ++button2IsPressed;
          for(int k=i+1; k<16; ++k){
          //if the buton1 is low then warrior shoots in the second row. otherwise it shoots in the first row
          if(buttonState1==LOW)
          lcd.setCursor(k,1);
          else lcd.setCursor(k,0);
          lcd.print("~");
          delay(5);
          if(buttonState1==LOW)
          lcd.setCursor(k-1,1);
          else lcd.setCursor(k-1,0);
          lcd.print(" ");
          delay(5);
          
     
          }
         }
         
           delay(myDelay);
          lcd.clear();
           //checking if the positions of the pts and the warrior are the same and if the button is pressed because then they collide and we gain 5 extra points
           for(int j=0; j<randomNum1; ++j){
          if(i==randomNums1[j] && buttonState1==HIGH){
           temp1=1;
           tempI1[j]=i;
           pts+=5;
           }
           }
        
             
      
      
          //checking if the positions of the obstacle and the warrior are the same and if the button1 is not pressed, then they collide and it is the end of the game
          for(int j=0; j<randomNum; ++j){
         if(i==randomNums[j] && buttonState1==LOW && temp==0) {
         
         pts=0;
         myDelay=500;
         lcd.clear();
         lcd.setCursor(6,0);
         lcd.print("GAME");
         lcd.setCursor(6,1);
         lcd.print("OVER");
         tone(buzzer,349);
         delay(200);
         tone(buzzer, 277);
         delay(200);
         tone(buzzer,220);
         delay(100);
         tone(buzzer,277);
         delay(100);
         tone(buzzer,220);
         delay(200);
         noTone(buzzer);
         delay(3000);
         lcd.clear();
         //i must skip the for loop because otherwise the game would continue where it ended
          goto here;
        
         }
         //counting number of skipped obstacles and that would be our points
         else if(i==randomNums[j] && buttonState1==HIGH) ++pts;
          }
       
          
         
      }



}

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

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

#include <LiquidCrystal_I2C.h>

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

LiquidCrystal_I2C lcd(0x27, 16, 2);

نعرف المتغيرات الخاصة بالأزرار buttonPin1 تم ربطه بالمنفذ 1 و buttonPin2 تم ربطه بالمنفذ 6 ومصدر الصوت buzzer تم ربطه بالمنفذ 7.

const int buttonPin1=1;
const int buttonPin2=6;
const int buzzer=7;

ستكون الحالة الابتدائية لكل الأزارير =0.

bool buttonState1=0;
bool buttonState2=0;

سيتم اختيار أماكن عشوائية لعلامة المربع على الشاشة الكرستالية.

//random number for position of obstacles
int randomNums[6];
//random number for number of obstacles
int randomNum=0;

وأماكن عشوائية لعلامة النجمة على الشاشة الكرستالية.

//random number for position of pts
int randomNums1[3];
//random number for number of pts
int randomNum1=0;

في الدالة ()setup ستستقبل الأزرار المدخلات والشاشة ومصدر الصوت للمخرجات.

ستحتوي الشاشة الابتدائية للعبة دولار نينجا على كلمة THE GAME ثم سيبدأ العدر التنازلي من 5 إلى 1 بعد ذلك ستبدأ اللعبة مباشرة.

void setup() 
{
  // set up the LCD's number of columns and rows:
  lcd.begin();
  // set buttonpin mode
  pinMode(buttonPin1,INPUT);
  pinMode(buttonPin2,INPUT);
  pinMode(buzzer,OUTPUT);
  lcd.setCursor(4,0);
  lcd.print("THE GAME");
  lcd.setCursor(2,1);
  lcd.print("STARTS IN ");
  lcd.print("5");
  delay(1000);
  lcd.setCursor(12,1);
  lcd.print("4");
  delay(1000);
  lcd.setCursor(12,1);
  lcd.print("3");
  delay(1000);
  lcd.setCursor(12,1);
  lcd.print("2");
  delay(1000);
  lcd.setCursor(12,1);
  lcd.print("1");
  delay(1000);
  lcd.clear();

}

في الدالة ()loop ستبدأ النجوم وعلامة المربع بالظهور بأماكن عشوائية متفرقة.

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

إذا لم يستطع الهروب وحدث تلامس بينه وبين علامة المربع ستنتهي اللعبة بخسارة اللاعب دولار وستظهر رسالة The game over.

void loop() 
{
  here:


  randomNum=random(5);
  for(int i=0; i<randomNum; ++i){
  randomNums[i]=random(16);}
   randomNum1=random(3);
  for(int i=0; i<randomNum1; ++i){
  randomNums1[i]=random(16);
  }
  //i reset temp variable before every new for loop because the cycle of the moving warrior starts again
  temp=0;
  for(int j=0; j<3; ++j){
  tempI1[j]=0;
  }
  button2IsPressed=0;
//loop that writes dollar(warrior) on the lcd which jumps everytime button is pressed
    for(int i=0; i<16; ++i){
          
          
          //pts must be written here because of the lcd.clear() below (pts wouldn't be written down the whole time otherwise)
          //i must check if pts is greater than 9 or 99 etc to know how many gaps i should  leave for the number. the more points are collected, the smaller delay time is and the faster is dollar
          if(pts>9 && pts<20) 
          {lcd.setCursor(14,0);
           myDelay=400;
          }
          else if(pts>19 && pts<30) {
            lcd.setCursor(14,0);
            myDelay=300;
          }
          else if(pts>29 && pts<50) 
          {
            myDelay=200;
            lcd.setCursor(14,0);
          }
          
          else if(pts>=50){
            pts=0;
            myDelay=500;
            lcd.clear();
            lcd.setCursor(5,0);
            lcd.print("VICTORY");
            tone(buzzer,262);
            delay(200);
            tone(buzzer,330);
            delay(200);
            tone(buzzer,392);
            delay(100);
            tone(buzzer,330);
            delay(100);
            tone(buzzer,392);
            delay(100);
            tone(buzzer,523);
            delay(200);
            noTone(buzzer);
            delay(3000);
            lcd.clear();
            goto here;
            }
          else lcd.setCursor(15,0);
          lcd.print(pts);
            
         buttonState1=digitalRead(buttonPin1);
         buttonState2=digitalRead(buttonPin2);
 //setting obstacles if the warrior didn't shoot or he shot but he also jumped
 if(!temp){
          for(int j=0; j<randomNum; ++j){
          lcd.setCursor(randomNums[j],1);
          lcd.print("#");
       }
 }
 //checking if the warrior  had shot but there were obstacles before him, we want those obstacles to stay 
 else{
  for(int j=0; j<randomNum; ++j){
 //i check only the tempI[0] position because that's the when the warrior shot for the first time and he removed all the remaining obstacles 
    if(tempI[0]>randomNums[j]){
      lcd.setCursor(randomNums[j],1);
      lcd.print("#");
    
      }

      }
  }        
          

//setting pts
if(!temp1){
          for(int j=0; j<randomNum1; ++j){
          lcd.setCursor(randomNums1[j],0);
          
          lcd.print("*");
           
          }
} 
else{
  for(int j=0; j<randomNum1; ++j){
  
   if(randomNums1[j]!=tempI1[j]){
    lcd.setCursor(randomNums1[j],0);
    lcd.print("*");
    }
  }
  }
           
          
        
          //if the button is pressed we set the cursor up (so that our warrior jumps)
          if(buttonState1==HIGH) 
          {lcd.setCursor(i,0);
          tone(buzzer,131);
          delay(200);
          noTone(buzzer);
          }
          else lcd.setCursor(i,1);
          
          lcd.print("$");
         
          //if button2 is pressed our warrior shoots. i had to put this loop here so that warrior shoots from his current spot and then continues to move. that's why delay time is 5 so that this loop finishes as soon as possible
           if (buttonState2==HIGH) 
         {
          tone(buzzer,175);
         delay(100);
         noTone(buzzer);
         
          temp=1;
          //if the button1 is low then we remove all the obstacles. that's why the state if temp variable is changed
          if(buttonState1==LOW) 
          {
            
            tempI[button2IsPressed]=i;
          }
          ++button2IsPressed;
          for(int k=i+1; k<16; ++k){
          //if the buton1 is low then warrior shoots in the second row. otherwise it shoots in the first row
          if(buttonState1==LOW)
          lcd.setCursor(k,1);
          else lcd.setCursor(k,0);
          lcd.print("~");
          delay(5);
          if(buttonState1==LOW)
          lcd.setCursor(k-1,1);
          else lcd.setCursor(k-1,0);
          lcd.print(" ");
          delay(5);
          
     
          }
         }
         
           delay(myDelay);
          lcd.clear();
           //checking if the positions of the pts and the warrior are the same and if the button is pressed because then they collide and we gain 5 extra points
           for(int j=0; j<randomNum1; ++j){
          if(i==randomNums1[j] && buttonState1==HIGH){
           temp1=1;
           tempI1[j]=i;
           pts+=5;
           }
           }
        
             
      
      
          //checking if the positions of the obstacle and the warrior are the same and if the button1 is not pressed, then they collide and it is the end of the game
          for(int j=0; j<randomNum; ++j){
         if(i==randomNums[j] && buttonState1==LOW && temp==0) {
         
         pts=0;
         myDelay=500;
         lcd.clear();
         lcd.setCursor(6,0);
         lcd.print("GAME");
         lcd.setCursor(6,1);
         lcd.print("OVER");
         tone(buzzer,349);
         delay(200);
         tone(buzzer, 277);
         delay(200);
         tone(buzzer,220);
         delay(100);
         tone(buzzer,277);
         delay(100);
         tone(buzzer,220);
         delay(200);
         noTone(buzzer);
         delay(3000);
         lcd.clear();
         //i must skip the for loop because otherwise the game would continue where it ended
          goto here;
        
         }
         //counting number of skipped obstacles and that would be our points
         else if(i==randomNums[j] && buttonState1==HIGH) ++pts;
          }
       
          
         
      }



}

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

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

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

 




تنصيب برنامج Arduino IDE على الراسبيري باي

مقدمة

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

برمجة الاردوينو

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

برمجة الاردوينو

 1× راسبيري باي

برمجة الاردوينو

 1× سلك (HDMI)

برمجة الاردوينو

1× سلك ايثرنت

برمجة الاردوينو

1× محول تيار (5V-2A)

برمجة الاردوينو

 1× كرت ذاكرة

program-your-arduino-from-your-raspberry-pi

 1× ثنائي مشع للضوء

program-your-arduino-from-your-raspberry-pi

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

program-your-arduino-from-your-raspberry-pi

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

توصيل القطع

1- الراسبيري باي

وصّل سلك الاردوينو في منفذ USB للراسبيري باي كما في الشكل.
برمج لوحة الاردوينو

2- الاردوينو

برمج لوحة الاردوينو

تهيئة الراسبيري باي

أولا، سوف تحتاج إلى تثبيت نظام الراسبيان على الراسبيري باي إذا لم تكن قد فعلت ذلك قم بالإطلاع على  الدرس الأول نظام تشغيل الراسبيري باي

وبعد تثبيت النظام، يمكنك تهيئة النظام من خلال الرجوع  للدرس الثاني تهيئة نظام التشغيل

افتح الشاشة السوداء LXterminal للبدء باستخدام الراسبيري باي.

قارئ النصوص

للبدء، قم بإستخدام الـ Termial لتحديث الراسبيري باي إلى أحدث إصدار.

sudo apt-get update
sudo apt-get upgrade

ثم أعد تشغيل الراسبيري باي.

reboot

سنبدأ بتنصيب برنامج اردوينو IDE.

افتح المتصفح.

اكتب بخانة البحث Arduino IDE وادخل على الموقع الرسمي.

برمج لوحة الاردوينو

حمّل النسخة Linux ARM 32 Bits.

اضغط على Just download ستبدأ عملية التنصيب مباشرة.

برمج لوحة الاردوينو

بعد اكتمال عملية التنصيب سنبدأ باستخراج الملف المضغوط.

انقر على أيقونة الراسبيان في أعلى يسار الصفحة من القائمة اخر Accessories ثم اختر Archiver.program-your-arduino-from-your-raspberry-pi

انقر على Archive ثم Open اختر الملف المضغوط الذي قمت بتنصيبه arduino-1.8.19-linuxarm.tar.xy.

انتظر حتى تتوقف الدائرة الحمراء الموجودة أسفل يمين الصفحة عن الوميض ويصبح لونها أخضر.
program-your-arduino-from-your-raspberry-pi

انقر على Action ثم Extract واختر الملف الذي قمت بفك الضغط عنه وانقر على Extract انتظر حتى تتوقف الدائرة الحمراء عن الوميض وتصبح خضراء.

program-your-arduino-from-your-raspberry-pi

افتح Terminal واكتب الأوامر التالية:

cd Downloads 
ls 
cd arduino-1.8.9
sudo ./install.sh

اذهب إل علامة الراسبيان أعلى يسار الشاشة من القائمة اختر Programming ستلاحظ وجود Arduino IDE انقر عليه سيبدأ بالعمل مباشرة.program-your-arduino-from-your-raspberry-pi

حدد منفذ الاردوينو اونو.

برمجة الاردوينو


برمجة الاردوينو

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

// the setup function runs once when you press reset or power the board
void setup() {
  // initialize digital pin LED_BUILTIN as an output.
  pinMode(LED_BUILTIN, OUTPUT);
}

// the loop function runs over and over again forever
void loop() {
  digitalWrite(LED_BUILTIN, HIGH);//turn the LED on (HIGH is the voltage level)
  delay(1000);                       // wait for a second
  digitalWrite(LED_BUILTIN, LOW);//turn the LED off by making the voltage LOW
  delay(1000);                       // wait for a second
}

اختبر خطواتك.

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




اصنع قارئ النصوص باستخدام الراسبيري باي

مقدمة

يمكن للراسبيري باي أن يقرأ النصوص التي تقوم بكتابتها، وبالتالي يمكنك صناعة  قارئ النصوص بنفسك باستخدام الراسبيري باي.

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

قارئ النصوص

 1× راسبيري باي

text-to-speech-converter-for-raspberry-pi

 1× سلك (HDMI)

text-to-speech-converter-for-raspberry-pi

1× سلك ايثرنت

قارئ للنصوص

1× محول تيار (5V-2A)

قارئ للنصوص

 1× كرت ذاكرة

قارئ النصوص

1× مكبر صوت

توصيل القطع

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

قارئ النصوص

تهيئة الراسبيري باي

أولا، سوف تحتاج إلى تثبيت نظام الراسبيان على الراسبيري باي إذا لم تكن قد فعلت ذلك قم بالإطلاع على  الدرس الأول نظام تشغيل الراسبيري باي

وبعد تثبيت النظام، يمكنك تهيئة النظام من خلال الرجوع  للدرس الثاني تهيئة نظام التشغيل

افتح الشاشة السوداء LXterminal للبدء باستخدام الراسبيري باي.

قارئ النصوص

للبدء، قم بإستخدام الـ Termial لتحديث الراسبيري باي إلى أحدث إصدار.

sudo apt-get update
sudo apt-get upgrade

ثم أعد تشغيل الراسبيري باي.

reboot

يوجد العديد من البرامج التي تقوم ببقراءة النصوص سنتعرف على أربعة منها يمكن اختيار المناسب لك:

قارئ النصوص: eSpeak 

استخدم الأمر التالي لتحميل الحزمة.

sudo apt-get install espeak

اختبر القارئ بكتابة أي جملة تخطر على بالك تأكد من وضعها بين علامة التنصيص.

espeak “Geeks”

يمكنك التعديل على قارئ النصوص:

eSpeak: تستخدم لبدء تشغيل البرنامج.

-ven+f2:

en تستخدم لتحديد اللغة هنا استخدمنا اللغة الانجليزية.

f2 تدل على أن الصوت هو صوت مرأة.

 تستطيع الاختيار والتنويع بين الأصوات سواء ذكر أو أنثى من خلال هذه الأرقام والرموز:

+m1,+m2,+m3,+m4,+m5,+m6,+m7, and +f1,+f2,+f3,+f4

-s150: هنا لتعديل سرعة الصوت، السرعة الافتراضية هي:  175.

-a 200: تدل على مستوى الصوت المستخدم وهو معدل بين 0 و 200.

-g10: تدل على مقدار التوقف أثناءالكلام بين كل كلمة والأخرى.

espeak -ven+f2 -k5 -s150 -a 200 -g10 "Hi, Geeks"

قارئ النصوص: Festival

استخدم الأمر التالي لتحميل الحزمة.

sudo apt-get install festival

اختبر القارئ بكتابة أي جملة تخطر على بالك تأكد من وضعها بين علامة التنصيص.

echo “Hi, Geeks” | festival --tts

قارئ النصوص: Pico

استخدم الأوامر التالية لتحميل الحزم المطلوبة.

wget http://ftp.us.debian.org/debian/pool/non-free/s/svox/libttspico0_1.0+git20130326-9_armhf.deb
wget http://ftp.us.debian.org/debian/pool/non-free/s/svox/libttspico-utils_1.0+git20130326-9_armhf.deb
sudo apt-get install -f ./libttspico0_1.0+git20130326-9_armhf.deb ./libttspico-utils_1.0+git20130326-9_armhf.deb

اختبر القارئ بكتابة أي جملة تخطر على بالك تأكد من وضعها بين علامة التنصيص.

pico2wave -w lookdave.wav "Hi, Grrks" && aplay lookdave.wav

تدعم Pico TTS حاليًا الإنجليزية البريطانية والإنجليزية الأمريكية والإسبانية والهولندية والفرنسية والإيطالية.

الرموز هي:  en-GB و en-US و es-ES و de-DE و fr-FR و it-IT.

pico2wave -l it-IT -w lookdave.wav "Hi, Welcome to Circuit Digest Tutorial" && aplay lookdave.wa

قارئ النصوص: PYTTSX3

استخدم الأمر التالي لتحميل الحزمة.

pip install pyttsx3

انشئ ملف باسم pyttsx3. 

nano pyttsx3.py

انسخ والصق الكود البرمجي التالي في الملف.

اختبر القارئ بكتابة أي جملة تخطر على بالك تأكد من وضعها بين علامة التنصيص.

import pyttsx3
engine = pyttsx3.init()
engine.say("Hi,Geeks")
engine.runAndWait()

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

python pyttsx3.py




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

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

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

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

 

راسبيري باي

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

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