هذا الدليل جزء من: دروس تعليمية حول تقنية الواي فاي LoRa 32
جميع الفيديوهات المتعلقة بجهاز Heltec WiFi LoRa 32 مرتبطة بهذه المجموعة. ستجد روابط الفيديوهات الأخرى أسفل هذه المقالة.
تحكم في محرك سيرفو من مسافة بعيدة! درس Arduino لهيليك WiFi LoRa 32 V3 (TX)
في هذا الدليل، نأخذ الرسومات الدقيقة من مشروع السيرفو Heltec ESP32 LoRa V3 ونستعرض كيف تعمل - دون إضافة أي كود إضافي. ستتعلم كيف يقرأ جهاز الإرسال المشفر الدوار، ويؤمن تلك الزاوية ويرسلها عبر LoRa، وكيف يقوم جهاز الاستقبال بفك تشفيرها ويوجه سيرفو صغير. جميع روابط الأجزاء والكود أدناه، وإذا قمت بالطلب من خلال روابط الشركاء لدينا، فإن ذلك يساعدنا على الاستمرار في إنشاء هذه الدروس.
تثبيت لوحات هيلتيك ESP32
قم بإضافة هذا المسار في تفضيلات بيئة تطوير Arduino الخاصة بك كما هو موضح في الفيديو:https://resource.heltec.cn/download/package_heltec_esp32_index.json
1. أجهزة الإرسال (TX) والإعداد
على جانب TX تحتاج إلى:
-
لوحة Heltec WiFi LoRa 32 V3 (داخل غلاف Meshnology N33، مزودة ببطارية سعة 3000 مللي أمبير)
-
مُشَفِّر دوار موصول بـ GPIO 6 (CLK)، GPIO 5 (DT)، GPIO 4 (SW)
-
شاشة OLED على I²C (SDA= 4، SCL= 15)
الرسم يبدأ بتضمين وتهيئة كل شيء تمامًا كما فيHeltec_ESP32_LoRa_V3_Sevo_TX_AiRotaryEncoder.ino:
cppCopyEdit#include "AiEsp32RotaryEncoder.h"
#include "HT_SSD1306Wire.h"
#include "LoRaWan_APP.h"
#include "mbedtls/aes.h"
// …
static SSD1306Wire display(0x3c, 500000, SDA_OLED, SCL_OLED, GEOMETRY, RST_OLED);
AiEsp32RotaryEncoder rotaryEncoder = AiEsp32RotaryEncoder(
PIN_A, PIN_B, SW_PIN, ROTARY_ENCODER_VCC_PIN, false, true, true);
const int homePosition = 90;
const int MAX_ANGLE = 180;
int servoAngel = homePosition;
فيsetup(), الرمز:
-
تشغيل على الشاشة، تعيين الخط
-
مكالمات
rotaryEncoder.begin(),rotaryEncoder.setup(readEncoderISR),rotaryEncoder.setBoundaries(0, MAX_ANGLE, true)وrotaryEncoder.setAcceleration(20) -
يعيد تعيين المشفر إلى
homePosition -
يبدأ تشغيل LoRa عبر
Mcu.begin(HELTEC_BOARD, SLOW_CLK_TPYE)ويعدّ إعدادRadioEvents، القناة، والمعايير تمامًا كما هو موضح في الرسم التخطيطي المقدم.
إرسال الزاوية بأمان
كل دورة حلقة تعملrotary_loop()، التي:
-
يقرأ المشفر في خدمة المقاطعة للوقت الحقيقي (ISR)
-
عندما
servoAngelالتغييرات، ويضعها في مخزن بيانات بحجم 16 بايت، ويقوم بتشفيرها باستخدام AES-128encryptAES()من الرسم التخطيطي)، ويدعوcppCopyEditRadio.Send(data, sizeof(data)); -
مجموعات
lora_idle = falseحتىOnTxDone()يُشغّل ويعيد تعيينه.
3. معدات وترتيب جهاز الاستقبال (RX)
على جانب RX تحتاج إلى:
-
لوحة هيلتيك واي فاي لوارا 32 V3 (نفس العلبة/البطارية)
-
ميكرو سيرفو (مثل SG90) على GPIO 6 (أو أي دبوس PWM تم اختباره)
-
شاشة OLED
الرسم التخطيطي فيHeltec_ESP32_LoRa_V3_Sevo_RX.inoيبدأ بـ:
cppCopyEdit#include <ESP32Servo.h>
#include "HT_SSD1306Wire.h"
#include "LoRaWan_APP.h"
#include "mbedtls/aes.h"
// …
const int servoPin = 6;
const int SERVO_DUTY_MIN = 400; // us
const int SERVO_DUTY_MAX = 2400; // us
Servo myservo;
int servoAngel = homePosition;
فيsetup(), it:
-
يعمل على Vext لوحدة العرض/LoRa (
VextON()) -
مكالمات
Radio.Init(&RadioEvents)ويقوم بتكوين RX بنفس معلمات LoRa -
يربط السيرفو بـ
myservo.attach(servoPin, SERVO_DUTY_MIN, SERVO_DUTY_MAX)ويتمركزه فيhomePosition.
4. استلام وفك تشفير وتشغيل السيرفو
الجوهر هو الـOnRxDone(uint8_t *payload, …)استدعاء:
cppCopyEditdecryptAES((uint8_t*)rxpacket, userKey);
if (isNumber(rxpacket)) {
servoAngel = atoi(rxpacket);
myservo.write(servoAngel);
delay(15);
}
Serial.println("Angle: " + String(servoAngel));
lora_idle = true;
يقوم بفك تشفير الكتلة التي حجمها 16 بايت، ويحولها إلى عدد صحيح، ويقوم بتحديث السيرفو على الفور.
5. دعم دبابيس PWM وضبط السيرفو
قمنا باختبار هذه الدبابيس ESP32 لإخراج PWM وجميعها تعمل على تشغيل سيرفو صغير:
CopyEdit1, 2, 3, 4, 5, 6, 19, 35, 36, 38, 39, 40, 41, 42, 45, 47, 48
لـ SG90 القياسي، يستخدم كودنا نطاق نبضات من400 ميكروثانية(0Ã'°) to2400 ميكروثانية(180 درجة)، مما يمنح حركة سلسة وكاملة دون اهتزاز.
6. مخطط الأسلاك
فيما يلي أماكن مخصصة حيث يمكنك وضع مخططات الإرسال والاستقبال الخاصة بك:


رمز وروابط الشركاء
جميع الرسومات المذكورة أعلاه متاحة للتنزيل في قسم "الكود والموارد" أدناه. إذا كنت ترغب في بناء هذا بنفسك، يرجى النظر في شراء وحدة Heltec LoRa32 V3، وعلبة Meshnology N33، وم encoder دوار، ومحرك SG90 من خلال روابطنا التابعة. لا يكلفك ذلك شيئًا إضافيًا ويساعدنا على الاستمرار في إنشاء دروس مجانية مثل هذه!
فصول الفيديو للرجوع إليها
-
00:00 المقدمة ونظرة عامة
-
مفاهيم التحكم عن بُعد
-
أساسيات الاتصال LoRa
-
00:23 عينة الأجهزة
-
عرض الحالات والبطاريات
-
01:03 ميزات الوحدة
-
01:42 المواصفات والاتصال
-
:54 تشغيل السيرفو
-
03:05 التوصيلات وتخطيط الدبابيس
-
09:35 وضع الهوائي
-
11:04 تجميع الحالة
-
:26 رفع الرسومات
-
35:09 اختبار المدى 1.2 كم
-
:38 اختبار النطاق 1.4 كم
-
ملخص الأداء 38:41
-
:04 الخاتمة والدعم
هذا الدليل هو جزء من: دروس تعليمية حول تقنية الواي فاي LoRa 32
- استخدام Heltec WiFi LoRa 32 V3 لنقل درجة الحرارة باستخدام DHT22 إلى مسافة 1.4 كم
- 13 ميل 20 كم بدون واي فاي؟ كيف أرسل LoRa الجهد عبر مسافات شاقة! (Heltec WiFi LoRa 32 V3)
- قم بتشغيل جهاز من مسافة 13 ميل (21 كم) - مشروع لوارا النهائي بعيدًا عن الشبكة مع واي فاي لوارا 32!
- نظام إنذار الباب عن بُعد من مسافة 13 ميل (21 كم) مع تقنية LoRa - خارج الشبكة! (Heltec WiFi LoRa 32 V3)
- مشروع جهاز التحكم عن بعد: وحدة Heltec LoRa 32 لمسافة 13 ميلاً دون واي فاي / دون شريحة SIM
- How to Use the Heltec LoRa CubeCell Development Board HTCC-AB01
/*
* ملف: Heltec_ESP32_LoRa_V3_Sevo_TX_AiRotaryEncoder.ino
* كتب في 24 يونيو، 2025 بواسطة أحمد شمشيري
*
* =====================================================================
* وصف كود أردوينو: نظام تحكم سيرفو لورا الآمن (TX)
* =====================================================================
*
* المكونات الصلبة:
* -------------------
* - المتحكم الرئيسي: Heltec WiFi LoRa 32 V3
* - العلبة: علبة Meshnology N33 مع بطارية 3000mAh
* - المدخل: مشفر دوار مع زر ضغط
* - التغذية الراجعة: شاشة OLED مدمجة
* - المخرج: محرك سيرفو + نقل لاسلكي عبر لورا
*
* وظائف النظام:
* -------------------
* [1] التحكم بواسطة المشفر الدوار:
* - دوران مع عقارب الساعة / ضد عقارب الساعة لضبط الزاوية المستهدفة (0°-180°)
* - عرض الزاوية في الوقت الفعلي على شاشة OLED
* - زر الضغط يعيد السيرفو إلى الوضع الرئيسى (افتراضي: 90°)
*
* [2] نقل لاسلكي آمن:
* - جميع قيم الزوايا مشفرة قبل نقلها عبر لورا
* - أمر الوضع الرئيسى يُنقل كحزمة آمنة خاصة
* - يستخدم نطاق لورا بتردد 433MHz لتوفير اتصالات موثوقة
*
* [3] إدارة الطاقة:
* - مُحسَّن لعملية البطارية (3000mAh)
* - أوضاع منخفضة الطاقة بين النقلات
*
* للحصول على تعليمات الإعداد الكاملة:
* يرجى مشاهدة الفيديو التعليمي على: https://youtu.be/EPynuJ7sasY
* =====================================================================
*
* شاهد الفيديو الكامل: https://youtu.be/EPynuJ7sasY
* صفحة الموارد: https://robojax.com/T635
*
* إخلاء المسؤولية:
* تم تقديم هذا الكود "كما هو" دون أي ضمان من أي نوع. لا يجوز تحميل المؤلف المسؤولية عن أي أضرار تنشأ عن استخدام هذا الكود.
*
* الرخصة:
* هذا العمل مرخص بموجب رخصة المشاع الإبداعي العامة GNU v3.0
* قد تتوفر إذنات تتجاوز نطاق هذه الرخصة على Robojax.com
*
* شروط المشاركة:
* يمكنك مشاركة ونسخ وتعديل هذا الكود لأغراض غير تجارية شريطة:
* 1. الاحتفاظ بهذا الملاحظ بالكامل مع الكود الأصلي
* 2. تضمين رابط Robojax.com الأصلي
* 3. الاحتفاظ برابط الفيديو التعليمي على يوتوب (إذا كان ذلك مناسبًا)
* 4. الإشارة بوضوح إلى أي تعديلات تم إجراؤها
*
* التعليمات الأصلية على: https://robojax.com/T635
* فيديو يوتيوب: https://youtu.be/EPynuJ7sasY
*/
#include <Wire.h>
#include "HT_SSD1306Wire.h"
#include "WiFi.h"
static SSD1306Wire display(0x3c, 500000, SDA_OLED, SCL_OLED, GEOMETRY_128_64, RST_OLED); // addr , freq , i2c group , resolution , rst
const int TX_POWER = 2; // dBm from 2 to 20. when powered via battery 2 to 14dBm is the best option
const int MAX_ANGLE = 180; // the most common is 180, but you can set it as needed
String labelAngle = "Angle";
const int homePosition = 90; // initial position
// endcoder
const int SW_PIN = 4; // define a pin for rotary encode switch
const int PIN_A = 6;
const int PIN_B = 5;
const int ANGLE_STEP = 6;
const bool debug= false; // to print debug data in serial moinitor set it to true, else false
int servoAngel = homePosition;
int oldAngleValue = servoAngel;
#include "mbedtls/aes.h" // for securing data
#include <cstring> // For memset, memcpy
mbedtls_aes_context aes;
const char *userKey = "hyhT676#h~_1a"; // Security key.
#include "LoRaWan_APP.h"
#include "AiEsp32RotaryEncoder.h"
#include "Arduino.h"
#define ROTARY_ENCODER_VCC_PIN -1
// instead of changing here, rather change numbers above
AiEsp32RotaryEncoder rotaryEncoder = AiEsp32RotaryEncoder(
PIN_A,
PIN_B,
SW_PIN,
ROTARY_ENCODER_VCC_PIN,
ANGLE_STEP);
#define RF_FREQUENCY 915432000 // Hz
#define TX_OUTPUT_POWER TX_POWER // dBm from 2 to 20. when powered via battery 2 to 14dBm
#define LORA_BANDWIDTH 0 // [0: 125 kHz,
// 1: 250 kHz,
// 2: 500 kHz,
// 3: Reserved]
#define LORA_SPREADING_FACTOR 7 // [SF7..SF12]
#define LORA_CODINGRATE 1 // [1: 4/5,
// 2: 4/6,
// 3: 4/7,
// 4: 4/8]
#define LORA_PREAMBLE_LENGTH 8 // Same for Tx and Rx
#define LORA_SYMBOL_TIMEOUT 0 // Symbols
#define LORA_FIX_LENGTH_PAYLOAD_ON false
#define LORA_IQ_INVERSION_ON false
#define RX_TIMEOUT_VALUE 1000
#define BUFFER_SIZE 64 // Define the payload size here
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 VextON(void);
void rotary_loop(); // prototyp function: rotary encoder
void IRAM_ATTR readEncoderISR(); // prototyp function: rotary encoder
void rotary_onButtonClick(); // prototyp function: rotary encoder
void setup() {
Serial.begin(115200);
Serial.println();
VextON();
delay(100);
// we must initialize rotary encoder
rotaryEncoder.begin();
rotaryEncoder.setup(readEncoderISR);
bool circleValues = false;
rotaryEncoder.setBoundaries(0, MAX_ANGLE, circleValues); // minValue, maxValue, circleValues true|false (when max go to min and vice versa)
/*
* Rotary acceleration introduced 25.2.2021.
* in case range to select is huge, for example - select a value between 0 and 1000 and we want 785
* without accelerateion you need long time to get to that number
* Using acceleration, faster you turn, faster will the value raise.
* For fine tuning slow down.
*/
// rotaryEncoder.disableAcceleration(); //acceleration is now enabled by default - disable if you dont need it
rotaryEncoder.setAcceleration(20); // or set the value - larger number = more accelearation; 0 or 1 means disabled acceleration
rotaryEncoder.reset(homePosition); // set home position
// Initialising the UI will init the display too.
display.init();
display.setFont(ArialMT_Plain_10);
// LoRa stuff
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 displayAngle() {
display.clear(); // Clear display before new content
// Line 1: Text: Angle
display.setTextAlignment(TEXT_ALIGN_LEFT);
// Line 2: Temperature value in 24pt font
display.setFont(ArialMT_Plain_24);
// Format
String angleString = String(servoAngel) + "°";
display.setFont(ArialMT_Plain_16);
display.drawString(0, 0, labelAngle);
display.setFont(ArialMT_Plain_24);
display.drawString(0, 15, angleString);
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 sendData()
{
String txData = String(servoAngel) ;
uint8_t data[BUFFER_SIZE];
memset(data, 0, sizeof(data)); // Zero-padding
strncpy((char*)data, txData.c_str(), sizeof(data) - 1); // Copy string safely
encryptAES(data, userKey); // Encrypt before sending
if(lora_idle == true)
{
// delay(1000);
Radio.Send(data, sizeof(data));
if(debug){
Serial.print("Sending: ");
Serial.println((char *)data);
}
lora_idle = false;
oldAngleValue =servoAngel; // keep record of angle change
}
Radio.IrqProcess( );
}
void loop() {
rotary_loop();
// clear the display
display.clear();
displayAngle();
if(oldAngleValue != servoAngel)
{
sendData();
}
// delay(100);
}
void OnTxDone( void )
{
if(debug){
Serial.println("TX done......");
}
lora_idle = true;
}
void OnTxTimeout( void )
{
Radio.Sleep( );
if(debug){
Serial.println("TX Timeout......");
}
lora_idle = true;
}
/*
* Converts a user-provided plaintext key into a fixed-length 16-byte (128-bit)
* or 32-byte (256-bit) key.
*/
void processKey(const char *userKey, uint8_t *processedKey, size_t keySize) {
memset(processedKey, 0, keySize); // Fill with zeros
size_t len = strlen(userKey);
if (len > keySize) len = keySize; // Truncate if too long
memcpy(processedKey, userKey, len); // Copy valid key part
}
/*
* Encrypts a 16-byte (one block) message using AES-128.
*/
void encryptAES(uint8_t *data, const char *key) {
uint8_t processedKey[16]; // 128-bit key
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);
}
/*
* Decrypts a 16-byte (one block) message using AES-128.
*/
void decryptAES(uint8_t *data, const char *key) {
uint8_t processedKey[16]; // 128-bit key
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);
}
void rotary_onButtonClick()
{
static unsigned long lastTimePressed = 0;
// ignore multiple press in that time milliseconds
if (millis() - lastTimePressed < 500)
{
return;
}
lastTimePressed = millis();
if(debug){
Serial.print("button pressed ");
Serial.print(millis());
Serial.println(" milliseconds after restart");
}
}
void rotary_loop()
{
// dont print anything unless value changed
if (rotaryEncoder.encoderChanged())
{
if(debug){
Serial.print("Value: ");
Serial.println(rotaryEncoder.readEncoder());
}
servoAngel = rotaryEncoder.readEncoder();
}
if (rotaryEncoder.isEncoderButtonClicked())
{
rotaryEncoder.reset(homePosition);
servoAngel = homePosition;
rotary_onButtonClick();
}
}
void IRAM_ATTR readEncoderISR()
{
rotaryEncoder.readEncoder_ISR();
}
Common Course Links
Common Course Files
الموارد والمراجع
-
خارجيرابط موقع Heltec WiFi Kit 32heltec.org
ملفات📁
لا توجد ملفات متاحة.