شِفر (کود) جستجو

استفاده از Heltec WiFi LoRa 32 V3 برای ارسال دما با استفاده از DHT22 تا فاصله ۱.۴ کیلومتر

استفاده از Heltec WiFi LoRa 32 V3 برای ارسال دما با استفاده از DHT22 تا فاصله ۱.۴ کیلومتر

در این آموزش، ما به بررسی نحوه استفاده از ماجیول Heltec WiFi LoRa 32 V3 برای انتقال داده‌های دما از حساس(حس کننده) DHT22 در فواصل طولانی می‌پردازیم و موفق به دستیابی به فواصل تا 1.4 کیلومتر می‌شویم. این قابلیت از طریق استفاده از فناوری LoRa امکان‌پذیر است که امکان ارتباط کم‌مصرف و با برد بلند را فراهم می‌کند. در پایان این راهنما، شما یک سیستم عملی خواهید داشت که می‌تواند خوانش‌های دما را به‌صورت بی‌سیم ارسال کند.

وای‌فای لورا ۳۲ V3 داخل مِش‌نولوژی N30 RX و TX

ما با مروری بر اجزای سخت‌افزاری involved in this project شروع خواهیم کرد، از جمله ماجیول Heltec WiFi LoRa 32 V3 و حساس(حس کننده) DHT22. پس از آن، به دستورالعمل‌های سیم‌کشی خواهیم پرداخت، جایی که خواهید آموخت چگونه این اجزا را به هم وصل کنید. در نهایت، کدی که برای عملیاتی کردن این سیستم نیاز است را بررسی خواهیم کرد. برای راهنمایی بصری، لطفاً به ویدیو در زمان‌های مختلف مراجعه کنید (در ویدیو در 00:00).

توضیحاتی درباره سخت‌افزار

اجزای اصلی این پروژه، ماجیول Heltec WiFi LoRa 32 V3 و حساس(حس کننده) دما و رطوبت DHT22 هستند. ماجیول Heltec دارای یک میکروکنترلر ESP32 است که امکان اتصال Wi-Fi و بلوتوث را در کنار ارتباط LoRa فراهم می‌کند. این امر گزینه‌های انعطاف‌پذیر برای انتقال داده را ممکن می‌سازد.

وای‌فای لورا ۳۲ V3 در داخل مش‌نولوژی N30 به‌ عنوان فرستنده دما

حساس(حس کننده) DHT22 یک حساس(حس کننده) دیجیتال است که خوانش های دقیقی از دما و رطوبت ارائه می دهد. این حساس(حس کننده) از طریق یک پایه خروجی دیجیتال با ESP32 ارتباط برقرار می کند که باعث می شود اتصال و استفاده از آن در پروژه های شما آسان باشد. این اجزاء به همراه هم یک سیستم قوی برای نظارت بی سیم بر دما تشکیل می دهند.

جزئیات برگه داده

تولیدکننده هلتک اتوماسیون
شماره قطعه WiFi LoRa 32 V3
ولتاژ منطق/ورودی و خروجی 3.3 ولت
ولتاژ ورودی ۳.۷-۴.۲ ولت
جریان خروجی (به ازای هر کانال) ~1 A
جریان اوج (به ازای هر کانال) ~2 آمپر
راهنمایی فرکانس PWM ۱ کیلوهرتز (معمولی)
آستانه‌های منطقی ورودی ۰.۷ ولت (بالا)، ۰.۳ ولت (پایین)
افت ولتاژ / RDS(on)/saturation ۰.۳ ولت (حداکثر)
محدودیت‌های حرارتی 85 درجه سانتی‌گراد (حداکثر)
بسته ماجیول PCB
یادداشت‌ها / واریانت‌ها گزینه‌های مختلف فرکانس موجود است (مثلاً ۴۳۳ مگاهرتز، ۸۶۸ مگاهرتز، ۹۱۵ مگاهرتز)

  • اطمینان حاصل کنید که DHT22 با 3.3V تغذیه شود، نه 5V.
  • از سطوح منطقی مناسب برای ارتباط بین ESP32 و DHT22 استفاده کنید.
  • در نظر داشته باشید که اگر با جریان‌های بالا به مدت طولانی فعالیت می‌کنید، از خنک‌کننده‌های حرارتی استفاده کنید.
  • اتصال آنتن برای LoRa را بررسی کنید تا دامنه را به حداکثر برسانید.
  • به مقررات فرکانس LoRa در منطقه خود توجه داشته باشید.

دستورالعمل‌های سیم‌کشی

Heltec_WiFi_loRa_32V3_DHT22_wiring

برای اتصال حساس(حس کننده) DHT22 به ماجیول Heltec WiFi LoRa 32 V3، ابتدا پایه VCC حساس(حس کننده) را به پایه ۳.۳ ولت ماجیول Heltec وصل کنید. سپس پایه GND DHT22 را به یکی از پایه‌های GND روی Heltec متصل کنید. پایه داده DHT22 باید به پایه GPIO 3 روی Heltec متصل شود.

اطمینان حاصل کنید که از یک مقاومت_PULLUP (حدود ۱۰ کیلو اهم) بین پایه داده و ولتاژ مثبت (VCC) استفاده کنید تا خوانش‌ها پایدار باشند. علاوه بر این، اطمینان حاصل کنید که آنتن لوRa به‌طور محکم متصل است تا محدوده انتقال افزایش یابد. اگر از منبع تغذیه خارجی استفاده می‌کنید، اطمینان حاصل کنید که ماجیول هل‌تک به درستی تغذیه می‌شود تا از بروز هرگونه مشکل عملیاتی جلوگیری شود.

نصب بردهای Heltec ESP32

این مسیر را به ترجیحات IDE آردوینو خود اضافه کنید همانطور که در ویدیو نشان داده شده است:https://resource.heltec.cn/download/package_heltec_esp32_index.json

مثال‌های شِفر (کود) و راهنمایی

کدهای زیر نشان می‌دهند که چگونه ماجیول هل‌تک را برای خواندن داده‌های دما از حساس(حس کننده) 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()روش. این داده‌ها را به صورت بی‌سیم به ماجیول دریافت‌کننده منتقل خواهد کرد.

لطفاً به شِفر (کود) کامل بارگذاری شده در زیر مقاله برای پیاده‌سازی دقیق مراجعه کنید.

نمایشگاه / چه انتظاری داشته باشیم

پس از اینکه همه چیز تنظیم شد و شِفر (کود) به ماجیول هل‌تک آپلود شد، باید شاهد نمایش دماها بر روی صفحه OLED باشید. سیستم داده‌های دما را ارسال می‌کند که می‌تواند توسط ماجیول هل‌تک دیگری که برای خواندن داده‌ها پیکربندی شده است، دریافت شود. شما می‌توانید با دور کردن گیرنده از فرستنده، فاصله را آزمایش کنید و حداکثر فاصله‌ای که به‌دست آمده را تأیید کنید (در ویدئو در دقیقه ۱:۳۰).

از افت‌های رایج مانند سیم‌کشی نادرست، منبع تغذیه ناکافی یا استفاده از فرکانس نادرست LoRa احتیاط کنید. اطمینان حاصل کنید که حساس(حس کننده) DHT22 به درستی کار می‌کند و آنتن متصل است تا حداکثر دامنه را داشته باشید.

زمان‌بندی ویدئو

  • ۰۰:۰۰ شروع
  • ۳:۵۱ مشخصات
  • صفحه مستندات 8:32
  • 9:52 بسته و باتری
  • ۱۲:۵۸ برای اولین بار روشنش می‌کنیم
  • کتابخانه را در حال نصب ۱۶:۳۷
  • 18:19 شِفر (کود) پایه فرستنده
  • ۱۹:۴۳ شِفر (کود) پایه گیرنده
  • ۲۰:۳۹ نمایش ارسال و دریافت پیام متن
  • شِفر (کود) نمایشی OLED 23:02
  • شِفر (کود) متن پایه در نمایشگر OLED 24:06
  • متن پایه در نمایش OLED
  • 26:58 خواندن دما با DHT22
  • دمای فرستنده LoRa و نمایشگر 28:49
  • ۳۰:۰۷ دما و نمایشگر گیرنده لوRa
  • 32:13 فعال‌سازی LED هنگام افزایش دما
  • آزمایش دامنه انتقال LoRa :26
  • ۳۵:۰۱ دسی بل میکرو وات و میلی وات

تصاویر

Heltec_WiFi_loRa_32V3_DHT22_wiring
Heltec_WiFi_loRa_32V3_DHT22_wiring
Wifi LoRa 32 V3 inside Meshnology N30 as transmitter of Temperature
Wifi LoRa 32 V3 inside Meshnology N30 as transmitter of Temperature
meshnology-N30-LoRa-v3-red-black
meshnology-N30-LoRa-v3-red-black
Wifi LoRa 32 V3 inside Meshnology N30 RX and TX
Wifi LoRa 32 V3 inside Meshnology N30 RX and TX
563-Printing Simple Text on the screen of WiFi LoRa 32 V3
زبان: C++
/*
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);

}
773-Transmitter Code for Heltec WiFi LoRa 32 V3 to send temperature using DHT11, DHT22
زبان: C++
/*
 * بر روی 27 مارس 2025 نوشته شده است  
 * نوشته احمد شمشیری برای www.Robojax.com  
 * دما و رطوبت را از طریق RF 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 // دی‌اچ‌تی۲۲ (ای‌ام۲۳۰۲)

DHT dht(DHTPIN, DHTTYPE);

float tempC, tempF;
int humidity ;

 // ۱=C
 // 2=F
 // ۳=C، رطوبت //فقط برای نمایش، نه برای انتقال
 // ۴=F، رطوبت //فقط برای نمایش، نه برای انتقال
 // 5=رطوبت فقط
int dataType = 2;
String labelTemp = "Temperature";
String labelHumidity = "Humidity";
const int TX_POWER = 2; // dBm از ۲ تا ۲۰. هنگامی که از باتری تغذیه می‌شود، ۲ تا ۱۴dBm بهترین گزینه است.

#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 // dBm از 2 تا 20. وقتی که با باتری تغذیه می‌شود 2 تا 14dBm

#define LORA_BANDWIDTH                              0 // [0: 125 کیلوهرتز،
 // ۱: ۲۵۰ کیلوهرتز،
 // ۲: ۵۰۰ کیلوهرتز،
 // ۳: رزرو شده
#define LORA_SPREADING_FACTOR                       7 // [SF7..SF12]
#define LORA_CODINGRATE                             1 // [1: 4/5,
 // ۲: ۴/۶،
 // ۳: ۴/۷،
 // ۴: ۴/۸
#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];

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);

 // در initializing رابط کاربری، نمایش نیز راه‌اندازی خواهد شد.
  display.init();

  display.setFont(ArialMT_Plain_10);
   dht.begin();

 // مواده لوRa
  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(); // نمایش واضح قبل از محتوای جدید

 // خط 1: "دمای:" با فونت 16pt
    display.setTextAlignment(TEXT_ALIGN_LEFT);


 // خط ۲: مقدار دما با فونت ۲۴ پوینت
    display.setFont(ArialMT_Plain_24);

 // دمای صحیح را با علامت واحد مناسب فرمت کنید
    String tempStringC = String(tempC, 1) + "°C"; // یک رقم اعشاری
    String tempStringF = String(tempF, 1)+ "°F"; // یک رقم اعشاری
    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) // پیش‌فرض Vext خاموش است
{
  pinMode(Vext,OUTPUT);
  digitalWrite(Vext, HIGH);
}

void sendData()
{


    String tempStringC = String(tempC, 1) + " °C"; // یک رقم اعشاری
    String tempStringF = String(tempF, 1)+ " °F"; // یک رقم اعشاری
    String tempStringHumidity = String(humidity)+ " % RH";
    String txData;
 // ۱=C
 // 2=F
 // ۳=C، رطوبت
 // 4=F, رطوبت
 // 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;
}


/*
 * یک کلید متنی ارائه شده توسط کاربر را به یک کلید با طول ثابت ۱۶ بایت (۱۲۸ بیت) یا ۳۲ بایت (۲۵۶ بیت) تبدیل می‌کند.
 */
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); // بخش کلید معتبر را کپی کنید
}

/*
 * یک پیام ۱۶ بیتی (یک بلوک) را با استفاده از AES-۱۲۸ رمزگذاری می‌کند.
 */
void encryptAES(uint8_t *data, const char *key) {
    uint8_t processedKey[16]; // کلید ۱۲۸ بیتی
    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);
}

/*
 * پیام ۱۶ بایتی (یک بلوک) را با استفاده از AES-128 رمزگشایی می‌کند.
 */
void decryptAES(uint8_t *data, const char *key) {
    uint8_t processedKey[16]; // کلید ۱۲۸ بیتی
    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);
}
867-Receiver Code for Heltec WiFi LoRa 32 to receive and display Temperature
زبان: C++
/*
Written on April 01, 2025
written by Ahmad Shamshiri for www.Robojax.com
this sktech receives the secure temperature or humidity from WiFi LoRa 32 and decrypts it
and displays it on the OLED. There is action feature to triggerd if the temperature is below triggerdValue
Watch full video explaination https://youtu.be/WkyQMXkQhE8
Resources page: https://robojax.com/tutorial_view.php?id=387
*/
#include <Arduino.h>
// Alert configuration
const String triggerdText = "Too High"; // Correct String type
const float triggerdValue = 90.0f; // exclusive (this value is not included)
const int triggerdYpos = 45;
const bool triggerdType = true;//true is > (greter than triggerdValue) and false is < (less  than triggerdValue)
const int triggerdOutputPin = 7;//GPIO07 goes HIGH when triggered
const bool triggerdBlink4Me= true;//should blink or not

#include "mbedtls/aes.h"
#include <cstring>  // For memset, memcpy
mbedtls_aes_context aes;
const char *userKey = "hyhT676#h~_876s"; //Security key

#define MIN_RSSI -120  // Worst possible signal
#define MAX_RSSI -50   // Best possible signal


// For a connection via I2C using the Arduino Wire include:
#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



#include "LoRaWan_APP.h"
#include "Arduino.h"


#define RF_FREQUENCY                                915000000 // Hz

#define TX_OUTPUT_POWER                             14        // dBm

#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                                 30 // Define the payload size here

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 seconds

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);

  // Initialising the UI will init the display too.
  display.init();

  display.setFont(ArialMT_Plain_10);
  pinMode(triggerdOutputPin, OUTPUT);
  //LoRa stuff blow this line
     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();  // 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, data1);

    // Line 2: Temperature value in 24pt font
    display.setFont(ArialMT_Plain_24);
    display.drawString(0, 20, data2);  // 
    displaySignalStrength(rssi);
   // display.display();  // Update OLED

    
}

void displayLine(String data, int y) {
    // Line 1: "Temperature:" in 16pt font
    display.setTextAlignment(TEXT_ALIGN_LEFT);
    display.setFont(ArialMT_Plain_10);

    if (triggerdBlink4Me) {
        // Blink effect: Display text, wait, clear text, wait, then return
        display.drawString(0, y, data);
        display.display();
        delay(500);  // Keep text visible for 500ms

        display.clear();  // Clear the screen
        display.display();
        delay(500);  // Keep screen blank for 500ms
    } 
    else {
        // Normal display without blinking
        display.drawString(0, y, data);
        display.display();
    }
}


void VextON(void)
{
  pinMode(Vext,OUTPUT);
  digitalWrite(Vext, LOW);
}

void VextOFF(void) //Vext default OFF
{
  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) {
    // Convert RSSI to percentage (0-100%)
    int percent = map(constrain(rssi, MIN_RSSI, MAX_RSSI), MIN_RSSI, MAX_RSSI, 0, 100);
    
    // Display at bottom right corner
    display.setTextAlignment(TEXT_ALIGN_RIGHT);
    display.setFont(ArialMT_Plain_16);
    
    // Create signal strength indicator
    String strength = String(percent) + "% [";
    for (int i = 0; i < 5; i++) {
        strength += (percent > (i * 20)) ? "|" : " ";
    }
    strength += "]";
    
    display.drawString(128, 45, strength); // Position at bottom-right
}

void noSignalCheck()
{
    // Automatic "No Signal" after timeout
    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();
    }
}

/**
 * Triggers an action based on a threshold comparison.
 *
 * @param type           - If true, triggers when floatValue **exceeds** triggerdValue.
 *                         If false, triggers when floatValue **falls below** triggerdValue.
 * @param floatValue     - The current measured value to compare.
 * @param triggerdValue  - The threshold value that determines the trigger condition.
 */
void triggerAction(float floatValue)
{
  if(triggerdType)
  {
    if (floatValue > triggerdValue) {
        displayLine(triggerdText, triggerdYpos);
        Serial.println(triggerdText);
        digitalWrite(triggerdOutputPin, HIGH);//turns triggerdOutputPin to HIGH
    }else{
        digitalWrite(triggerdOutputPin, LOW);
    }
  }else{
    if (floatValue < triggerdValue) {
        displayLine(triggerdText, triggerdYpos);
        Serial.println(triggerdText);
        digitalWrite(triggerdOutputPin, HIGH);//turns triggerdOutputPin to HIGH
    } else{
        digitalWrite(triggerdOutputPin, LOW);//turns triggerdOutputPin to LOW
    }   
  }
}
void OnRxDone( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr )
{
    lastRxTime = millis(); // Reset timer on new data
    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);
    // Serial.println("Encrypted Data (Hex):");
    // for (int i = 0; i < 16; i++) {
    //     Serial.printf("%02X ", rxpacket[i]);
    // }
    // Serial.println();
   decryptAES((uint8_t *)rxpacket, userKey);

    // Split the received packet into parts
    String receivedStr = String((char*)rxpacket);
    int firstSpacePos = receivedStr.indexOf(' ');
    if (firstSpacePos != -1) {
        // First part (before first space)
        String part1 = receivedStr.substring(0, firstSpacePos);  // "Temperature"
        
        // Find second space (after the number)
        int secondSpacePos = receivedStr.indexOf(' ', firstSpacePos + 1);
        
        if (secondSpacePos != -1) {
            // Second part (numeric value)
            String part2 = receivedStr.substring(firstSpacePos + 1, secondSpacePos); // "34.5"
            float floatValue = part2.toFloat();  // Convert to float 34.5
            
            // Third part (unit)
            String part3 = receivedStr.substring(secondSpacePos + 1); // "°C"
            
            displayTemperature(part1, part2+part3);
            display.display();
            Serial.print("part1: " + part1);
            Serial.print(" part2: " + part2);
            Serial.println(" part3: " + part3);
            Serial.println("floatValue " + String(floatValue));          
        
        //to trigger an action. 
        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;
}

/**
 * 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);
}

Common Course Files

فایل‌ها📁

سایر فایل‌ها