هذا الدليل جزء من: دروس تعليمية حول تقنية الواي فاي LoRa 32
جميع الفيديوهات المتعلقة بجهاز Heltec WiFi LoRa 32 مرتبطة بهذه المجموعة. ستجد روابط الفيديوهات الأخرى أسفل هذه المقالة.
استخدام Heltec WiFi LoRa 32 V3 لنقل درجة الحرارة باستخدام DHT22 إلى مسافة 1.4 كم
في هذا الدليل، سنتعرف على كيفية استخدام وحدة Heltec WiFi LoRa 32 V3 لنقل بيانات درجة الحرارة من مستشعر DHT22 على مسافات طويلة، حيث يمكن تحقيق نطاق يصل إلى 1.4 كيلومتر. هذه القدرة ممكنة من خلال استخدام تقنية LoRa، التي تسمح بالتواصل منخفض الطاقة بعيدة المدى. بنهاية هذا الدليل، سيكون لديك نظام يعمل يمكنه إرسال قراءات درجة الحرارة لاسلكياً.
سنبدأ بنظرة عامة على مكونات الأجهزة المشاركة في هذا المشروع، بما في ذلك وحدة Heltec WiFi LoRa 32 V3 ومستشعر DHT22. بعد ذلك، سنتوجه إلى تعليمات التوصيل، حيث ستتعلم كيفية ربط هذه المكونات. أخيرًا، سنستعرض الكود المطلوب لجعل هذا النظام عمليًا. للحصول على إرشادات بصرية، يرجى الرجوع إلى الفيديو في أوقات زمنية مختلفة (في الفيديو عند 00:00).
شرح الأجهزة
المكونات الرئيسية لهذا المشروع هي وحدة Heltec WiFi LoRa 32 V3 ومستشعر درجة الحرارة والرطوبة DHT22. تتميز وحدة Heltec بوجود متحكم دقيق ESP32، الذي يوفر قدرات الواي فاي والبلوتوث جنبًا إلى جنب مع الاتصال LoRa. وهذا يسمح بخيارات مرنة لنقل البيانات.
جهاز الاستشعار DHT22 هو جهاز استشعار رقمي يوفر قراءات دقيقة لدرجة الحرارة والرطوبة. يتواصل مع ESP32 من خلال دبوس إخراج رقمي واحد، مما يسهل توصيله واستخدامه في مشاريعك. معًا، تشكل هذه المكونات نظامًا قويًا لمراقبة درجة الحرارة لاسلكيًا.
تفاصيل ورقة البيانات
| الشركة المصنعة | هلبتك أوتوميشن |
|---|---|
| رقم الجزء | WiFi LoRa 32 V3 |
| جهد المنطق/الإدخال والإخراج | ٣٫٣ فولت |
| جهد الإمداد | ٣.٧-٤.٢ ف |
| التيار الخارجي (لكل قناة) | ~1 أ |
| تيار الذروة (لكل قناة) | ~2 أ |
| توجيه تردد PWM | 1 كيلو هرتز (تقريباً) |
| عتبات منطق الإدخال | ٠.٧ فولت (مرتفع)، ٠.٣ فولت (منخفض) |
| انخفاض الجهد / المقاومةدS(on)/ تشبع | 0.3 فولت (أقصى) |
| الحدود الحرارية | 85 °م (最大) |
| حزمة | وحدة PCB |
| ملاحظات / متنوعات | خيارات ترددات متنوعة متاحة (مثل 433 ميجاهرتز، 868 ميجاهرتز، 915 ميجاهرتز) |
- تأكد من تشغيل DHT22 بجهد 3.3 فولت، وليس 5 فولت.
- استخدم مستويات منطقية مناسبة للتواصل بين ESP32 وDHT22.
- اعتبر استخدام تبديد الحرارة إذا كنت تعمل عند تيارات عالية لفترات طويلة.
- تحقق من اتصال الهوائي لـ LoRa لتعظيم المدى.
- كن حذرًا من لوائح ترددات LoRa في منطقتك.
تعليمات التوصيل

لتوصيل جهاز Heltec WiFi LoRa 32 V3 مع مستشعر DHT22، ابدأ بتوصيل دبوس VCC الخاص بالمستشعر بدبوس 3.3V على وحدة Heltec. بعد ذلك، قم بتوصيل دبوس GND الخاص بـ DHT22 بأحد دبابيس GND على Heltec. يجب توصيل دبوس البيانات الخاص بـ DHT22 بدبوس GPIO 3 على Heltec.
تأكد من استخدام مقاومة سحب (حوالي 10kΩ) بين دبوس البيانات و VCC لقراءات مستقرة. بالإضافة إلى ذلك، تأكد من أن هوائي LoRa متصل بشكل آمن لتحسين نطاق الإرسال. إذا كنت تستخدم طاقة خارجية، فتأكد من أن وحدة Heltec تتلقى الطاقة بشكل صحيح لتجنب أي مشاكل في التشغيل.
تثبيت لوحات هيلتيك ESP32
قم بإضافة هذا المسار إلى تفضيلات IDE الخاص بـ Arduino كما هو موضح في الفيديو:https://resource.heltec.cn/download/package_heltec_esp32_index.json
أمثلة على الشيفرة وجولة توضيحية
توضح مقتطفات الشيفرة التالية كيفية إعداد وحدة Heltec لقراءة بيانات درجة الحرارة من مستشعر DHT22 ونقلها عبر LoRa. تقوم الشيفرة بتهيئة الشاشة وإعداد مستشعر DHT.
#include
#define DHTPIN 3 // GPIO pin for DHT22
#define DHTTYPE DHT22 // Define DHT type
DHT dht(DHTPIN, DHTTYPE);
void setup() {
Serial.begin(115200);
dht.begin(); // Initialize DHT sensor
}في هذه الشريحة، نحدد الدبوس الذي يتصل به مستشعر DHT22 ونقوم بتهيئته في الـsetup()لا شيءSerial.begin(115200)السطر مخصص لإخراج تصحيح الأخطاء.
void loop() {
float tempC = dht.readTemperature(); // Read temperature in Celsius
float tempF = dht.convertCtoF(tempC); // Convert to Fahrenheit
sendData(tempC, tempF); // Function to send temperature data
}توضح هذه الفقرة كيفية قراءة بيانات درجة الحرارة في الـloop()وظيفة. الsendData()يتم استدعاء الدالة لنقل قراءات درجة الحرارة عبر LoRa.
void sendData(float tempC, float tempF) {
String data = "Temperature: " + String(tempC) + "°C"; // Create data string
Radio.Send(data.c_str(), data.length()); // Send data
}هنا، نقوم بإنشاء سلسلة البيانات التي تحتوي على درجة الحرارة وإرسالها باستخدام الـRadio.Send()الطريقة. ستقوم هذه بنقل البيانات لاسلكيًا إلى الوحدة المستقبلة.
يرجى الرجوع إلى الشيفرة الكاملة المحملة أسفل المقال للحصول على تنفيذ مفصل.
عرض / ما يمكن توقعه
بمجرد إعداد كل شيء وتحميل الشيفرة على وحدة Heltec، يجب أن ترى قراءات درجة الحرارة معروضة على شاشة OLED. سيقوم النظام بنقل بيانات درجة الحرارة، والتي يمكن استقبالها بواسطة وحدة Heltec أخرى تم تكوينها لقراءة البيانات. يمكنك اختبار النطاق من خلال تحريك جهاز الاستقبال بعيدًا عن جهاز الإرسال، مما يؤكد المسافة القصوى التي تم تحقيقها (في الفيديو عند 1:30).
كن حذرًا من الأخطاء الشائعة مثل الأسلاك الخاطئة، ووجود مصدر طاقة غير كافٍ، أو استخدام تردد LoRa غير الصحيح. تأكد من أن DHT22 يعمل بشكل صحيح وأن الهوائي متصل لتعظيم النطاق.
طوابق الفيديو
- 00:00 بدء
- مواصفات 3:51
- صفحة الوثائق 8:32
- ٩:٥٢ الحزمة والبطارية
- 12:58 تشغيله لأول مرة
- 16:37 تثبيت المكتبة
- 18:19 الكود الأساسي لجهاز الإرسال
- 19:43 رمز أساسي للمستلم
- 20:39 عرض إرسال واستقبال النص
- ٢٣:٠٢ كود عرض OLED
- 24:06 نص أساسي على كود عرض OLED
- نص أساسي حول عرض OLED
- 26:58 قراءة درجة الحرارة باستخدام DHT22
- 28:49 درجة حرارة جهاز إرسال LoRa وشاشة العرض
- 30:07 درجة حرارة مستقبل LoRa والعرض
- 32:13 تشغيل LED عند زيادة درجة الحرارة
- اختبار مدى نقل LoRa 22:26
- 35:01 ديسيبل ميلي وات
هذا الدليل هو جزء من: دروس تعليمية حول تقنية الواي فاي LoRa 32
- 13 ميل 20 كم بدون واي فاي؟ كيف أرسل LoRa الجهد عبر مسافات شاقة! (Heltec WiFi LoRa 32 V3)
- قم بتشغيل جهاز من مسافة 13 ميل (21 كم) - مشروع لوارا النهائي بعيدًا عن الشبكة مع واي فاي لوارا 32!
- نظام إنذار الباب عن بُعد من مسافة 13 ميل (21 كم) مع تقنية LoRa - خارج الشبكة! (Heltec WiFi LoRa 32 V3)
- تحكم في محرك سيرفو من مسافة بعيدة! درس Arduino لهيليك WiFi LoRa 32 V3 (TX)
- مشروع جهاز التحكم عن بعد: وحدة Heltec LoRa 32 لمسافة 13 ميلاً دون واي فاي / دون شريحة SIM
- How to Use the Heltec LoRa CubeCell Development Board HTCC-AB01
/*
This is a simple code to display text on the OLED display
WiFi LoRa 32 V3 ESP32 module
Written by Ahmad Shamshiri 02 April 2025
Watch full video explanation https://youtu.be/WkyQMXkQhE8
Resources page https://robojax.com/tutorial_view.php?id=387
*/
#include <Wire.h>
#include "HT_SSD1306Wire.h"
static SSD1306Wire display(0x3c, 500000, SDA_OLED, SCL_OLED, GEOMETRY_128_64, RST_OLED); // addr , freq , i2c group , resolution , rst
void setup() {
Serial.begin(115200);
Serial.println();
Serial.println();
VextON();
delay(100);
// Initialising the UI will init the display too.
display.init();
display.setFont(ArialMT_Plain_10);
}
void displayTemperature(double temperature, int unit) {
display.clear(); // Clear display before new content
// Line 1: "Temperature:" in 16pt font
display.setTextAlignment(TEXT_ALIGN_LEFT);
display.setFont(ArialMT_Plain_16);
display.drawString(0, 0, "Temperature:");
// Line 2: Temperature value in 24pt font
display.setFont(ArialMT_Plain_24);
// Format temperature with correct unit symbol
String tempString = String(temperature, 1); // 1 decimal place
switch(unit) {
case 1: tempString += "�C"; break; // Celsius
case 2: tempString += "�F"; break; // Fahrenheit
default: tempString += "�U"; break; // Unknown unit
}
display.drawString(0, 20, tempString); // Display at Y=20 (below label)
display.display(); // Update OLED
}
void VextON(void)
{
pinMode(Vext,OUTPUT);
digitalWrite(Vext, LOW);
}
void VextOFF(void) //Vext default OFF
{
pinMode(Vext,OUTPUT);
digitalWrite(Vext, HIGH);
}
void loop() {
// clear the display
display.clear();
displayTemperature(23.5, 1); // Displays "23.5�C" /1
delay(2000);
}
/*
* مكتوب في 27 مارس 2025
* مكتوب بواسطة أحمد شماشيري لموقع www.Robojax.com
* ينقل درجة الحرارة والرطوبة عبر ترددات LoRa باستخدام وحدة ESP32 LoRA 32 V3.
* ويعرض المعلومات على الشاشة.
* شاهد شرح الفيديو الكامل https://youtu.be/WkyQMXkQhE8
* صفحة الموارد: https://robojax.com/tutorial_view.php?id=387
*/
#include <Wire.h>
#include "HT_SSD1306Wire.h"
static SSD1306Wire display(0x3c, 500000, SDA_OLED, SCL_OLED, GEOMETRY_128_64, RST_OLED); // عنوان ، تردد ، مجموعة I2C ، دقة ، إعادة تعيين
#include <DHT.h>
#define DHTPIN 3 // GPIO21
#define DHTTYPE DHT22 // DHT22 (AM2302)
DHT dht(DHTPIN, DHTTYPE);
float tempC, tempF;
int humidity ;
// 1=C
// 2=ف
// 3=C، رطوبة //للعرض فقط وليس للإرسال
// 4=ف، الرطوبة //للعرض فقط وليس للإرسال
// 5=الرطوبة فقط
int dataType = 2;
String labelTemp = "Temperature";
String labelHumidity = "Humidity";
const int TX_POWER = 2; // ديسيبل مِلّي واط من 2 إلى 20. عند التشغيل بواسطة البطارية، 2 إلى 14 ديسيبل مِلّي واط هو الخيار الأفضل.
#include "mbedtls/aes.h"
#include <cstring> // لـ memset و memcpy
mbedtls_aes_context aes;
const char *userKey = "hyhT676#h~_876s"; // مفتاح الأمان.
#include "LoRaWan_APP.h"
#include "Arduino.h"
#define RF_FREQUENCY 915000000 // هرتز
#define TX_OUTPUT_POWER TX_POWER // ديسيبل ملي واط من 2 إلى 20. عند التشغيل عبر البطارية 2 إلى 14 ديسيبل ملي واط
#define LORA_BANDWIDTH 0 // 125 كيلوهرتز
// 250 كيلو هرتز،
// 500 كيلوهرتز،
// محجوز 3
#define LORA_SPREADING_FACTOR 7 // [SF7..SF12]
#define LORA_CODINGRATE 1 // 4/5
// ٢: ٤/٦،
// ٣: ٤/٧،
// ٤: ٤/٨
#define LORA_PREAMBLE_LENGTH 8 // نفس الشيء بالنسبة للإرسال والاستقبال
#define LORA_SYMBOL_TIMEOUT 0 // رموز
#define LORA_FIX_LENGTH_PAYLOAD_ON false
#define LORA_IQ_INVERSION_ON false
#define RX_TIMEOUT_VALUE 1000
#define BUFFER_SIZE 30 // حدد حجم الحمولة هنا
char txpacket[BUFFER_SIZE];
char rxpacket[BUFFER_SIZE];
double txNumber;
bool lora_idle=true;
static RadioEvents_t RadioEvents;
unsigned long lastTxTime = 0;
void OnTxDone( void );
void OnTxTimeout( void );
void decryptAES(uint8_t *data, const char *key);
void encryptAES(uint8_t *data, const char *key);
void processKey(const char *userKey, uint8_t *processedKey, size_t keySize);
void setup() {
Serial.begin(115200);
Serial.println();
VextON();
delay(100);
// تهيئة واجهة المستخدم ستقوم أيضًا بتهيئة العرض.
display.init();
display.setFont(ArialMT_Plain_10);
dht.begin();
// أشياء لورا
Mcu.begin(HELTEC_BOARD,SLOW_CLK_TPYE);
txNumber=0;
RadioEvents.TxDone = OnTxDone;
RadioEvents.TxTimeout = OnTxTimeout;
Radio.Init( &RadioEvents );
Radio.SetChannel( RF_FREQUENCY );
Radio.SetTxConfig( MODEM_LORA, TX_OUTPUT_POWER, 0, LORA_BANDWIDTH,
LORA_SPREADING_FACTOR, LORA_CODINGRATE,
LORA_PREAMBLE_LENGTH, LORA_FIX_LENGTH_PAYLOAD_ON,
true, 0, 0, LORA_IQ_INVERSION_ON, 3000 );
}
void displayTemperature(int unit) {
display.clear(); // مسح العرض قبل المحتوى الجديد
// درجة الحرارة:
display.setTextAlignment(TEXT_ALIGN_LEFT);
// خط 2: قيمة درجة الحرارة بخط 24 نقطة
display.setFont(ArialMT_Plain_24);
// تنسيق درجة الحرارة مع رمز الوحدة الصحيح
String tempStringC = String(tempC, 1) + "°C"; // 1 عشرة عشرية
String tempStringF = String(tempF, 1)+ "°F"; // 1 عشرة عشرية
String tempStringHumidity = String(humidity)+ "% RH";
String tempString;
switch(unit) {
case 1:
tempString =tempStringC;
display.setFont(ArialMT_Plain_16);
display.drawString(0, 0, "Temperature:");
display.setFont(ArialMT_Plain_24);
display.drawString(0, 15, tempString);
break; // سيلسيوس
case 2: tempString =tempStringF;
display.setFont(ArialMT_Plain_16);
display.drawString(0, 0, "Temperature:");
display.setFont(ArialMT_Plain_24);
display.drawString(0, 15, tempString);
break; // فارنهايت
case 3: tempString =tempStringC;
display.setFont(ArialMT_Plain_16);
display.drawString(0, 0, "Temperature:");
display.setFont(ArialMT_Plain_24);
display.drawString(0, 15, tempString);
display.setFont(ArialMT_Plain_16);
display.drawString(0, 40, "Humidity:");
display.drawString(70, 40, tempStringHumidity);
break; // سيلسيوس
case 4: tempString =tempStringF;
display.setFont(ArialMT_Plain_16);
display.drawString(0, 0, "Temperature:");
display.setFont(ArialMT_Plain_24);
display.drawString(0, 15, tempString);
display.setFont(ArialMT_Plain_16);
display.drawString(0, 40, "Humidity:");
display.drawString(70, 40, tempStringHumidity );
break; // سيلسيوس
case 5:
display.setFont(ArialMT_Plain_16);
display.drawString(0, 0, "Humidity:");
display.setFont(ArialMT_Plain_24);
display.drawString(0, 20, tempStringHumidity);
break; // سيلسيوس
default: tempString =tempStringC + "°C"; break;; // افتراضي
}
display.display(); // تحديث OLED
}
void readSensor()
{
tempC = dht.readTemperature();
humidity = dht.readHumidity();
tempF = dht.convertCtoF(tempC);
}
void VextON(void)
{
pinMode(Vext,OUTPUT);
digitalWrite(Vext, LOW);
}
void VextOFF(void) // فيكسيت الوضع الافتراضي إيقاف
{
pinMode(Vext,OUTPUT);
digitalWrite(Vext, HIGH);
}
void sendData()
{
String tempStringC = String(tempC, 1) + " °C"; // 1 عشرة عشرية
String tempStringF = String(tempF, 1)+ " °F"; // 1 عشرة عشرية
String tempStringHumidity = String(humidity)+ " % RH";
String txData;
// 1=C
// 2=ف
// 3=C، الرطوبة
// ٤=ف، رطوبة
// 5=الرطوبة فقط
switch(dataType) {
case 1:
txData = labelTemp + " " + tempStringC;
break;
case 2:
txData = labelTemp + " " + tempStringF;
break;
case 3:
txData = labelHumidity + " " + tempStringHumidity;
break;
default:
txData = labelTemp + " " + tempStringC;
break;
}
uint8_t data[32];
memset(data, 0, sizeof(data)); // صفر-padding
strncpy((char*)data, txData.c_str(), sizeof(data) - 1); // نسخ السلسلة بأمان
encryptAES(data, userKey); // قم بالتشفير قبل الإرسال
if(lora_idle == true)
{
delay(1000);
Radio.Send(data, sizeof(data));
Serial.print("Sending: ");
Serial.println((char *)data);
lora_idle = false;
}
Radio.IrqProcess( );
}
void loop() {
readSensor(); // اقرأ البيانات
// امسح العرض
display.clear();
displayTemperature(dataType);
sendData();
delay(100);
}
void OnTxDone( void )
{
Serial.println("TX done......");
lora_idle = true;
}
void OnTxTimeout( void )
{
Radio.Sleep( );
Serial.println("TX Timeout......");
lora_idle = true;
}
/*
* يحول مفتاح النص العادي المقدم من المستخدم إلى مفتاح ثابت الطول يبلغ 16 بايت (128 بت) أو 32 بايت (256 بت).
*/
void processKey(const char *userKey, uint8_t *processedKey, size_t keySize) {
memset(processedKey, 0, keySize); // املأ بالصفر
size_t len = strlen(userKey);
if (len > keySize) len = keySize; // قُم بتقليص إذا كان طويلاً جداً
memcpy(processedKey, userKey, len); // نسخ جزء مفتاح صالح
}
/*
* يشفّر رسالة بحجم 16 بايت (كتلة واحدة) باستخدام AES-128.
*/
void encryptAES(uint8_t *data, const char *key) {
uint8_t processedKey[16]; // مفتاح 128 بت
processKey(key, processedKey, 16);
mbedtls_aes_init(&aes);
mbedtls_aes_setkey_enc(&aes, processedKey, 128);
mbedtls_aes_crypt_ecb(&aes, MBEDTLS_AES_ENCRYPT, data, data);
mbedtls_aes_free(&aes);
}
/*
* يفك تشفير رسالة بطول 16 بايت (كتلة واحدة) باستخدام AES-128.
*/
void decryptAES(uint8_t *data, const char *key) {
uint8_t processedKey[16]; // مفتاح 128 بت
processKey(key, processedKey, 16);
mbedtls_aes_init(&aes);
mbedtls_aes_setkey_dec(&aes, processedKey, 128);
mbedtls_aes_crypt_ecb(&aes, MBEDTLS_AES_DECRYPT, data, data);
mbedtls_aes_free(&aes);
}
/*
* كتب في 01 أبريل 2025
* كتب بواسطة أحمد شمشيري لموقع www.Robojax.com
* هذا الرسم يتلقى درجة الحرارة أو الرطوبة الآمنة من WiFi LoRa 32 ويقوم بفك تشفيرها
* ويعرضها على شاشة OLED. هناك ميزة عمل يتم تفعيلها إذا كانت درجة الحرارة أقل من القيمة المحددة
* شاهد الفيديو الكامل للشرح https://youtu.be/WkyQMXkQhE8
* صفحة الموارد: https://robojax.com/tutorial_view.php?id=387
*/
#include <Arduino.h>
// تكوين التنبيهات
const String triggerdText = "Too High"; // سلسلة صحيحة النوع
const float triggerdValue = 90.0f; // حصري (هذه القيمة غير مشمولة)
const int triggerdYpos = 45;
const bool triggerdType = true; // صحيح هو > (أكبر من القيمة المُتحَتَث) وخاطئ هو < (أقل من القيمة المُتحَتَث)
const int triggerdOutputPin = 7; // GPIO07 يرتفع عندما يتم تشغيله
const bool triggerdBlink4Me= true; // يجب أن يومض أم لا
#include "mbedtls/aes.h"
#include <cstring> // بالنسبة لـ memset و memcpy
mbedtls_aes_context aes;
const char *userKey = "hyhT676#h~_876s"; // مفتاح الأمان
#define MIN_RSSI -120 // أسوأ إشارة ممكنة
#define MAX_RSSI -50 // أفضل إشارة ممكنة
// للإتصال عبر I2C باستخدام Arduino Wire، قم بتضمين:
#include <Wire.h>
#include "HT_SSD1306Wire.h"
static SSD1306Wire display(0x3c, 500000, SDA_OLED, SCL_OLED, GEOMETRY_128_64, RST_OLED); // عنوان ، تردد ، مجموعة i2c ، دقة ، إعادة تعيين
#include "LoRaWan_APP.h"
#include "Arduino.h"
#define RF_FREQUENCY 915000000 // هيرتز
#define TX_OUTPUT_POWER 14 // ديسيبل مللي واط
#define LORA_BANDWIDTH 0 // 125 كيلو هرتز،
// 250 كيلوهرتز،
// 2: 500 كيلو هرتز،
// 3: محجوزة
#define LORA_SPREADING_FACTOR 7 // [SF7..SF12]
#define LORA_CODINGRATE 1 // [1: ٤/٥,
// ٢: ٤/٦،
// ٣: ٤/٧،
// ٤: ٤/٨
#define LORA_PREAMBLE_LENGTH 8 // نفس الشيء بالنسبة للمرسل (Tx) والمستقبل (Rx)
#define LORA_SYMBOL_TIMEOUT 0 // رموز
#define LORA_FIX_LENGTH_PAYLOAD_ON false
#define LORA_IQ_INVERSION_ON false
#define RX_TIMEOUT_VALUE 1000
#define BUFFER_SIZE 30 // حدد حجم الحمولة هنا
char txpacket[BUFFER_SIZE];
char rxpacket[BUFFER_SIZE];
static RadioEvents_t RadioEvents;
int16_t txNumber;
int16_t rssi,rxSize;
bool lora_idle = true;
unsigned long lastRxTime = 0;
const unsigned long SIGNAL_TIMEOUT = 5000; // 5 ثواني
void decryptAES(uint8_t *data, const char *key);
void encryptAES(uint8_t *data, const char *key);
void processKey(const char *userKey, uint8_t *processedKey, size_t keySize);
void setup() {
Serial.begin(115200);
Serial.println();
Serial.println();
VextON();
delay(100);
// تهيئة واجهة المستخدم ستقوم أيضًا بتهيئة العرض.
display.init();
display.setFont(ArialMT_Plain_10);
pinMode(triggerdOutputPin, OUTPUT);
// أشياء LoRa تحت هذا السطر
Mcu.begin(HELTEC_BOARD,SLOW_CLK_TPYE);
txNumber=0;
rssi=0;
RadioEvents.RxDone = OnRxDone;
Radio.Init( &RadioEvents );
Radio.SetChannel( RF_FREQUENCY );
Radio.SetRxConfig( MODEM_LORA, LORA_BANDWIDTH, LORA_SPREADING_FACTOR,
LORA_CODINGRATE, 0, LORA_PREAMBLE_LENGTH,
LORA_SYMBOL_TIMEOUT, LORA_FIX_LENGTH_PAYLOAD_ON,
0, true, 0, 0, LORA_IQ_INVERSION_ON, true );
}
void displayTemperature(String data1, String data2) {
display.clear(); // قم بمسح العرض قبل المحتوى الجديد
// درجة الحرارة:
display.setTextAlignment(TEXT_ALIGN_LEFT);
display.setFont(ArialMT_Plain_16);
display.drawString(0, 0, data1);
// القيمة الحرارية بخط بحجم 24 نقطة
display.setFont(ArialMT_Plain_24);
display.drawString(0, 20, data2);
displaySignalStrength(rssi);
// عرض.عرض(); // تحديث OLED
}
void displayLine(String data, int y) {
// درجة الحرارة:
display.setTextAlignment(TEXT_ALIGN_LEFT);
display.setFont(ArialMT_Plain_10);
if (triggerdBlink4Me) {
// تأثير الوميض: عرض النص، الانتظار، مسح النص، الانتظار، ثم العودة
display.drawString(0, y, data);
display.display();
delay(500); // ابق النص مرئيًا لمدة 500 مللي ثانية
display.clear(); // امسح الشاشة
display.display();
delay(500); // ابق الشاشة فارغة لمدة 500 مللي ثانية
}
else {
// عرض طبيعي دون وميض
display.drawString(0, y, data);
display.display();
}
}
void VextON(void)
{
pinMode(Vext,OUTPUT);
digitalWrite(Vext, LOW);
}
void VextOFF(void) // إيقاف تشغيل Vext افتراضيًا
{
pinMode(Vext,OUTPUT);
digitalWrite(Vext, HIGH);
}
void loop() {
RaIrqProcessdio( );
if(lora_idle)
{
lora_idle = false;
Serial.println("into RX mode");
Radio.Rx(0);
}
noSignalCheck();
delay(100);
}
void displaySignalStrength(int16_t rssi) {
// تحويل RSSI إلى نسبة مئوية (0-100%)
int percent = map(constrain(rssi, MIN_RSSI, MAX_RSSI), MIN_RSSI, MAX_RSSI, 0, 100);
// عرض في الزاوية السفلى اليمنى
display.setTextAlignment(TEXT_ALIGN_RIGHT);
display.setFont(ArialMT_Plain_16);
// إنشاء مؤشر قوة الإشارة
String strength = String(percent) + "% [";
for (int i = 0; i < 5; i++) {
strength += (percent > (i * 20)) ? "|" : " ";
}
strength += "]";
display.drawString(128, 45, strength); // موضع في أسفل اليمين
}
void noSignalCheck()
{
// إشارة "لا توجد إشارة" تلقائيًا بعد انتهاء الوقت المحدد
if (millis() - lastRxTime > SIGNAL_TIMEOUT) {
display.clear();
display.setTextAlignment(TEXT_ALIGN_CENTER);
display.setFont(ArialMT_Plain_16);
display.drawString(64, 20, "No Signal");
display.display();
}
}
/*
* يؤدي إلى إجراء استنادًا إلى مقارنة العتبة.
*
* @param النوع - إذا كان صحيحًا، يتم تفعيل الإجراء عندما **يتجاوز** floatValue قيمة triggerdValue. إذا كان خطأ، يتم تفعيل الإجراء عندما **ينخفض** floatValue عن قيمة triggerdValue.
* @param floatValue - القيمة الحالية المقاسة للمقارنة.
* @param triggerdValue - القيمة الحدية التي تحدد شرط التفعيل.
*/
void triggerAction(float floatValue)
{
if(triggerdType)
{
if (floatValue > triggerdValue) {
displayLine(triggerdText, triggerdYpos);
Serial.println(triggerdText);
digitalWrite(triggerdOutputPin, HIGH); // يحول إخراج الدبوس إلى HIGH
}else{
digitalWrite(triggerdOutputPin, LOW);
}
}else{
if (floatValue < triggerdValue) {
displayLine(triggerdText, triggerdYpos);
Serial.println(triggerdText);
digitalWrite(triggerdOutputPin, HIGH); // يحول إخراج الدبوس إلى HIGH
} else{
digitalWrite(triggerdOutputPin, LOW); // يحول الدبوس المخرج إلى المستوى المنخفض
}
}
}
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr )
{
lastRxTime = millis(); // إعادة تعيين المؤقت على البيانات الجديدة
rssi=rssi;
rxSize=size;
memcpy(rxpacket, payload, size );
rxpacket[size]='\0';
Radio.Sleep( );
Serial.printf("\r\nreceived packet \"%s\" with rssi %d , length %d\r\n",rxpacket,rssi,rxSize);
// سيريل.println("بيانات مشفرة (هيكس):");
// for (int i = 0; i < 16; i++) {
// Serial.printf("%02X ", rxpacket[i]);
// }
// Serial.println();
decryptAES((uint8_t *)rxpacket, userKey);
// قم بتقسيم الحزمة المستلمة إلى أجزاء
String receivedStr = String((char*)rxpacket);
int firstSpacePos = receivedStr.indexOf(' ');
if (firstSpacePos != -1) {
// الجزء الأول (قبل أول فراغ)
String part1 = receivedStr.substring(0, firstSpacePos); // درجة الحرارة
// اعثر على المسافة الثانية (بعد الرقم)
int secondSpacePos = receivedStr.indexOf(' ', firstSpacePos + 1);
if (secondSpacePos != -1) {
// الجزء الثاني (قيمة عددية)
String part2 = receivedStr.substring(firstSpacePos + 1, secondSpacePos); // "34.5"
float floatValue = part2.toFloat(); // قم بتحويل 34.5 إلى عدد عشري
// الجزء الثالث (الوحدة)
String part3 = receivedStr.substring(secondSpacePos + 1); // "°م"
displayTemperature(part1, part2+part3);
display.display();
Serial.print("part1: " + part1);
Serial.print(" part2: " + part2);
Serial.println(" part3: " + part3);
Serial.println("floatValue " + String(floatValue));
// لإحداث فعل.
triggerAction(floatValue);
} else {
Serial.println("No second space found for unit");
}
}else {
Serial.println("No space found in packet - can't split");
}
lora_idle = true;
}
/*
* يحويل مفتاح نصي يقدمه المستخدم إلى مفتاح ثابت الطول يتكون من 16 بايت (128 بت) أو 32 بايت (256 بت).
*/
void processKey(const char *userKey, uint8_t *processedKey, size_t keySize) {
memset(processedKey, 0, keySize); // املأ بالصفر
size_t len = strlen(userKey);
if (len > keySize) len = keySize; // إقطع إذا كانت طويلة جدًا
memcpy(processedKey, userKey, len); // نسخ جزء المفتاح الصالح
}
/*
* يشفّر رسالة بطول 16 بايت (كتلة واحدة) باستخدام AES-128.
*/
void encryptAES(uint8_t *data, const char *key) {
uint8_t processedKey[16]; // مفتاح 128 بت
processKey(key, processedKey, 16);
mbedtls_aes_init(&aes);
mbedtls_aes_setkey_enc(&aes, processedKey, 128);
mbedtls_aes_crypt_ecb(&aes, MBEDTLS_AES_ENCRYPT, data, data);
mbedtls_aes_free(&aes);
}
/*
* يفك تشفير رسالة بطول 16 بايت (كتلة واحدة) باستخدام AES-128.
*/
void decryptAES(uint8_t *data, const char *key) {
uint8_t processedKey[16]; // مفتاح 128 بت
processKey(key, processedKey, 16);
mbedtls_aes_init(&aes);
mbedtls_aes_setkey_dec(&aes, processedKey, 128);
mbedtls_aes_crypt_ecb(&aes, MBEDTLS_AES_DECRYPT, data, data);
mbedtls_aes_free(&aes);
}
Common Course Links
Common Course Files
الموارد والمراجع
-
خارجياشترِ Wi-Fi LoRa 32 من ميشنولوجيmeshnology.com
-
خارجي
-
خارجي
-
خارجي
-
خارجي
ملفات📁
ملفات أخرى
-
مخطط الدائرة لهالة واي فاي لوارا 32 V3 (الإصدار 3.1)
Heltec_WiFiLoRAV3_Schematic_Diagram.pdf0.18 MB