Search Code

Sử dụng Heltec WiFi LoRa 32 V3 để truyền nhiệt độ bằng DHT22 đến khoảng cách 1,4km

Bài học này là một phần của: Giới thiệu về WiFi LoRa

Sử dụng Heltec WiFi LoRa 32 V3 để truyền nhiệt độ bằng DHT22 đến khoảng cách 1,4km

Trong hướng dẫn này, chúng ta sẽ khám phá cách sử dụng mô-đun Heltec WiFi LoRa 32 V3 để truyền dữ liệu nhiệt độ từ cảm biến DHT22 qua khoảng cách xa, đạt phạm vi lên tới 1,4 km. Khả năng này có được nhờ sử dụng công nghệ LoRa, cho phép liên lạc tầm xa, năng lượng thấp. Đến cuối hướng dẫn này, bạn sẽ có một hệ thống hoạt động có thể gửi số liệu nhiệt độ không dây.

Wifi LoRa 32 V3 bên trong Meshnology N30 RX và TX

Chúng ta sẽ bắt đầu với phần tổng quan về các thành phần phần cứng liên quan đến dự án này, bao gồm mô-đun Heltec WiFi LoRa 32 V3 và cảm biến DHT22. Sau đó, chúng ta sẽ chuyển sang phần hướng dẫn đấu dây, nơi bạn sẽ học cách kết nối các thành phần này. Cuối cùng, chúng ta sẽ xem qua mã cần thiết để làm cho hệ thống này hoạt động. Để có hướng dẫn trực quan, vui lòng tham khảo video tại các mốc thời gian khác nhau (trong video tại 00:00).

Giải thích về Phần cứng

Các thành phần chính cho dự án này là mô-đun Heltec WiFi LoRa 32 V3 và cảm biến nhiệt độ, độ ẩm DHT22. Mô-đun Heltec có vi điều khiển ESP32, cung cấp khả năng Wi-Fi và Bluetooth cùng với liên lạc LoRa. Điều này cho phép các tùy chọn truyền dữ liệu linh hoạt.

Wifi LoRa 32 V3 bên trong Meshnology N30 như máy phát nhiệt độ

Cảm biến DHT22 là cảm biến kỹ thuật số cung cấp số liệu nhiệt độ và độ ẩm chính xác. Nó giao tiếp với ESP32 thông qua một chân đầu ra kỹ thuật số duy nhất, giúp dễ dàng kết nối và sử dụng trong các dự án của bạn. Cùng với nhau, các thành phần này tạo thành một hệ thống mạnh mẽ để giám sát nhiệt độ không dây.

Chi tiết Bảng dữ liệu

Nhà sản xuất Heltec Automation
Số bộ phận WiFi LoRa 32 V3
Điện áp Logic/IO 3,3 V
Điện áp cung cấp 3,7–4,2 V
Dòng điện đầu ra (mỗi kênh) ~1 A
Dòng điện đỉnh (mỗi kênh) ~2 A
Hướng dẫn tần số PWM 1 kHz (điển hình)
Ngưỡng logic đầu vào 0,7 V (cao), 0,3 V (thấp)
Sụt áp / RDS(on) / bão hòa 0,3 V (tối đa)
Giới hạn nhiệt 85 °C (tối đa)
Kiểu đóng gói Mô-đun PCB
Ghi chú / biến thể Có nhiều tùy chọn tần số khác nhau (ví dụ: 433 MHz, 868 MHz, 915 MHz)

 

  • Đảm bảo cấp nguồn cho DHT22 bằng 3,3V, không phải 5V.
  • Sử dụng mức logic phù hợp để giao tiếp giữa ESP32 và DHT22.
  • Cân nhắc tản nhiệt nếu chạy ở dòng điện cao trong thời gian dài.
  • Kiểm tra kết nối ăng-ten cho LoRa để tối đa hóa phạm vi.
  • Lưu ý các quy định về tần số LoRa trong khu vực của bạn.

Hướng dẫn Đấu dây

Heltec_WiFi_loRa_32V3_DHT22_wiring

Để đấu dây Heltec WiFi LoRa 32 V3 với cảm biến DHT22, hãy bắt đầu bằng cách kết nối chân VCC của cảm biến với chân 3.3V trên mô-đun Heltec. Tiếp theo, kết nối chân GND của DHT22 với một trong các chân GND trên Heltec. Chân dữ liệu của DHT22 nên được kết nối với chân GPIO 3 trên Heltec.

Đảm bảo sử dụng điện trở kéo lên (khoảng 10kΩ) giữa chân dữ liệu và VCC để có số liệu ổn định. Ngoài ra, hãy đảm bảo ăng-ten LoRa được kết nối chắc chắn để tăng cường phạm vi truyền. Nếu bạn đang sử dụng nguồn ngoài, hãy đảm bảo mô-đun Heltec được cấp nguồn chính xác để tránh các sự cố vận hành.

Cài đặt Bo mạch Heltec ESP32

Thêm đường dẫn này vào tùy chọn của Arduino IDE của bạn như được hiển thị trong video:https://resource.heltec.cn/download/package_heltec_esp32_index.json

Cài đặt Thư viện Heltec ESP32

TRONG Arduino IDE khi đã mở, vui lòng nhấp vào biểu tượng Thư viện (1), sau đó gõ "Heltec" vào ô tìm kiếm (2) và nhấp Cài đặt (3) xem hình ảnh. 

Cài đặt thư viện cho bo mạch Heltec ESP342

Ví dụ Mã & Hướng dẫn Chi tiết

Các đoạn mã sau minh họa cách thiết lập mô-đun Heltec để đọc dữ liệu nhiệt độ từ cảm biến DHT22 và truyền qua LoRa. Mã khởi tạo màn hình và thiết lập cảm biến DHT.

#include 
#define DHTPIN 3         // Chân GPIO cho DHT22
#define DHTTYPE DHT22    // Xác định loại DHT
DHT dht(DHTPIN, DHTTYPE);
void setup() {
  Serial.begin(115200);
  dht.begin(); // Khởi tạo cảm biến DHT
}

Trong đoạn mã này, chúng ta xác định chân mà cảm biến DHT22 được kết nối và khởi tạo nó trong hàm setup(). Dòng Serial.begin(115200) dùng để xuất thông tin gỡ lỗi.

void loop() {
  float tempC = dht.readTemperature(); // Đọc nhiệt độ theo độ C
  float tempF = dht.convertCtoF(tempC); // Chuyển đổi sang độ F
  sendData(tempC, tempF); // Hàm gửi dữ liệu nhiệt độ
}

Đoạn trích này cho thấy cách đọc dữ liệu nhiệt độ trong hàm loop(). Hàm sendData() được gọi để truyền số liệu nhiệt độ qua LoRa.

void sendData(float tempC, float tempF) {
  String data = "Nhiệt độ: " + String(tempC) + "°C"; // Tạo chuỗi dữ liệu
  Radio.Send(data.c_str(), data.length()); // Gửi dữ liệu
}

Ở đây, chúng ta tạo chuỗi dữ liệu chứa nhiệt độ và gửi nó bằng phương thức Radio.Send(). Điều này sẽ truyền dữ liệu không dây đến mô-đun nhận.

Vui lòng tham khảo mã đầy đủ được tải bên dưới bài viết để biết chi tiết triển khai.

Trình diễn / Điều mong đợi

Khi mọi thứ được thiết lập và mã được tải lên mô-đun Heltec, bạn sẽ thấy các chỉ số nhiệt độ hiển thị trên màn hình OLED. Hệ thống sẽ truyền dữ liệu nhiệt độ, dữ liệu này có thể được nhận bởi một mô-đun Heltec khác được cấu hình để đọc dữ liệu. Bạn có thể kiểm tra phạm vi bằng cách di chuyển bộ thu ra xa bộ phát, xác nhận khoảng cách tối đa đạt được (trong video tại 1:30).

Hãy cẩn thận với các lỗi thường gặp như đấu dây sai, nguồn điện không đủ hoặc sử dụng sai tần số LoRa. Đảm bảo DHT22 hoạt động bình thường và ăng-ten được kết nối để tối đa hóa phạm vi.

Mốc thời gian video

  • 00:00 Bắt đầu
  • 3:51 Thông số kỹ thuật
  • 8:32 Trang tài liệu
  • 9:52 Gói hàng và pin
  • 12:58 Bật nguồn lần đầu tiên
  • 16:37 Cài đặt thư viện
  • 18:19 Mã cơ bản bộ phát
  • 19:43 Mã cơ bản bộ thu
  • 20:39 Trình diễn gửi nhận văn bản
  • 23:02 Mã demo OLED
  • 24:06 Mã văn bản cơ bản trên màn hình OLED
  • 26:26 Demo văn bản cơ bản trên OLED
  • 26:58 Đọc nhiệt độ bằng DHT22
  • 28:49 Bộ phát LoRa nhiệt độ và hiển thị
  • 30:07 Bộ thu LoRa nhiệt độ và hiển thị
  • 32:13 Kích hoạt LED khi nhiệt độ tăng
  • 22:26 Kiểm tra phạm vi truyền LoRa
  • 35:01 dBm và Milli Watt

Hình ảnh

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
Installing libraries for Heltec ESP342 boards
Installing libraries for Heltec ESP342 boards
563-Printing Simple Text on the screen of WiFi LoRa 32 V3
Ngôn ngữ: 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
Ngôn ngữ: C++
/*
written on March 27, 2025
written by Ahmad Shamshiri for www.Robojax.com
Transmits Temperature and Humidity over LoRa RF using ESP32 LoRA 32 V3 module.
and displays the information on the screen.
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

#include <DHT.h>
#define DHTPIN 3         // GPIO21
#define DHTTYPE DHT22     // DHT22 (AM2302)

DHT dht(DHTPIN, DHTTYPE);

float tempC, tempF;
int humidity ;

  //1=C
  //2=F
  //3=C, Humidity //only for display not for transmission
  //4=F, Humidity //only for display not for transmission
  //5=Humidity only
int dataType = 2;
String labelTemp = "Temperature";
String labelHumidity = "Humidity";
const int TX_POWER = 2;//dBm from 2 to 20. when powered via battery 2 to 14dBm is the best option

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


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


#define RF_FREQUENCY                                915000000 // 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                                 30 // 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 setup() {
  Serial.begin(115200);
  Serial.println();

  VextON();
  delay(100);

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

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

  //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 displayTemperature(int unit) {
    display.clear();  // Clear display before new content
    
    // Line 1: "Temperature:" in 16pt font
    display.setTextAlignment(TEXT_ALIGN_LEFT);


    // Line 2: Temperature value in 24pt font
    display.setFont(ArialMT_Plain_24);
    
    // Format temperature with correct unit symbol
    String tempStringC = String(tempC, 1) + "°C"; // 1 decimal place
    String tempStringF = String(tempF, 1)+ "°F"; // 1 decimal place
    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;  // Celsius
        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;  // Fahrenheit
        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;  // Celsius    
        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;  // Celsius       
        case 5: 
          display.setFont(ArialMT_Plain_16);
          display.drawString(0, 0, "Humidity:");
          display.setFont(ArialMT_Plain_24); 
          display.drawString(0, 20, tempStringHumidity);         
          break;  // Celsius                     
        default: tempString =tempStringC + "°C"; break;;  // default
    }
    

    display.display();  // Update 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 default OFF
{
  pinMode(Vext,OUTPUT);
  digitalWrite(Vext, HIGH);
}

void sendData()
{
  

    String tempStringC = String(tempC, 1) + " °C"; // 1 decimal place
    String tempStringF = String(tempF, 1)+ " °F"; // 1 decimal place
    String tempStringHumidity = String(humidity)+ " % RH";   
    String txData; 
    //1=C
    //2=F
    //3=C, Humidity
    //4=F, Humidity
    //5=Humidity only
        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));  // 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));
      Serial.print("Sending: ");
      Serial.println((char *)data);
      lora_idle = false;
    }
    Radio.IrqProcess( );  
}



void loop() {
  readSensor();//read the data

  // clear the display
  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;
}


/**
 * 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);
}
867-Receiver Code for Heltec WiFi LoRa 32 to receive and display Temperature
Ngôn ngữ: 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);
}

Tài nguyên & tài liệu tham khảo

Tập tin📁

Thư viện Arduino (tệp nén)

Các tập tin khác