Hi ESPHome,
Short version
I need help with LORA configuration.
I have for the last 10 months been digging into ESPHome like crazy, I love it that I can easily create my own ESP32 devices that can get data from different sensors (temp, humidity, CO2, binary door, reversible fan), with/without a battery, so many things I can do.
In short ESPHome rocks!
In ESPHome 2025.7.0 SX126x Component was added to the list and my heart jumped into hypermode, I finally would be able to utilize and create my 1-10km away sensors from around the neighborhood.
Quickly bought one more Heltec Lora v3 (had one in storage) and started configuring.
After lots of tinkering, ChatGPG, CoPilot and Gemini I ended up with a working solution that I later improved on and on.
From here on out it will go down in proper acronyms, units and I ask you all for a better knowing and getting things right. I need help.
Sadly the receiver side never goes better then -111.0 dBm with SNR 8.25dB.
My working solution gives me -68.0 dBm on the sender side with SNR 7.75 dB.
But this is when the devices are 10-15cm away from each other.
As soon as I put my receiver where it suppose to be and using the MikroTik 868 Omni antenna.
I have tried with different chips/ESP32 and they all have the same issue.
I have connected the antenna connector all the time (no burnt chip).
My configs
Sender Heltec Lora V3:
substitutions:
name_s01: esp32-lora-sender-01
friendly_name: ESP32-Lora-Sender-01
esphome:
name: ${name_s01}
name_add_mac_suffix: false
friendly_name: ${friendly_name}
on_boot:
priority: 1000
then:
- output.turn_on: vext_power
esp32:
board: esp32-s3-devkitc-1
framework:
type: esp-idf
logger:
hardware_uart: UART0
level: debug
api:
encryption:
key: !secret esp32-lora-sender-01-encryption-key
ota:
- platform: esphome
password: !secret esp32-lora-sender-01-ota-password
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
ssid: "Esp32-Lora-Sender-01"
password: !secret esp32-lora-sender-01-ap-password
captive_portal:
spi:
clk_pin: GPIO9
mosi_pin: GPIO10
miso_pin: GPIO11
sx126x:
cs_pin: GPIO8
rst_pin: GPIO12
busy_pin: GPIO13
dio1_pin: GPIO14
rf_switch: True
id: lora
hw_version: sx1262
modulation: LORA
frequency: 868800000 #868800000
crc_enable: true
#rx_start: true
tcxo_voltage: 1_8V
tcxo_delay: 30ms
sync_value: [0x14, 0x24]
spreading_factor: 9 # Higher SF = better range and SNR
pa_power: 22 # Max out transmit power
bandwidth: 125_0kHz # Narrower bandwidth = better sensitivity
coding_rate: CR_4_5 # Stronger error correction
preamble_size: 12
on_packet:
then:
- lambda: |-
id(rssi_value) = rssi;
id(snr_value) = snr;
ESP_LOGD("lambda", "RSSI: %.2f dBm", rssi);
ESP_LOGD("lambda", "SNR: %.2f dB", snr);
packet_transport:
platform: sx126x
# encryption: !secret encryption_key
# rolling_code_enable: true
#ping_pong_enable: true
sensors:
- esp32_lora_sender_01_temperature
- esp32_lora_sender_01_humidity
- esp32_lora_sender_01_rssi
- esp32_lora_sender_01_snr
i2c:
# Separate I2C bus for the OLED display
- id: oled_i2c_bus
sda: GPIO17
scl: GPIO18
scan: True
# Separate I2C bus for the SHT3XD sensor
- id: sht3xd_i2c_bus
sda: GPIO41
scl: GPIO42
scan: true
output:
- platform: gpio
pin:
number: GPIO36
inverted: true
id: vext_power
sensor:
- platform: sht3xd
i2c_id: sht3xd_i2c_bus # Using the dedicated sensor bus
address: 0x44
update_interval: 10s
temperature:
name: "Temperature"
id: esp32_lora_sender_01_temperature
unit_of_measurement: "°C"
accuracy_decimals: 2
on_value:
then:
- lambda: |-
ESP_LOGI("LoRa", "Temperature to transmit: %.2f °C", x);
humidity:
name: "Humidity"
id: esp32_lora_sender_01_humidity
unit_of_measurement: "%"
accuracy_decimals: 2
on_value:
then:
- lambda: |-
ESP_LOGI("LoRa", "Humidity to transmit: %.2f %%", x);
- platform: template
name: "LoRa RSSI"
id: esp32_lora_sender_01_rssi
unit_of_measurement: "dBm"
accuracy_decimals: 1
lambda: |-
return id(rssi_value);
- platform: template
name: "LoRa SNR"
id: esp32_lora_sender_01_snr
unit_of_measurement: "dB"
accuracy_decimals: 2
lambda: |-
return id(snr_value);
- platform: adc
pin: GPIO1
id: battery_voltage
update_interval: 30s
attenuation: 12db
filters:
- lambda: |-
float raw_adc = x;
float voltage = raw_adc * 4.1; // Instead of 3.3
int percent = int((voltage - 2.5) * 100.0 / (4.2 - 2.5));
percent = std::max(0, std::min(percent, 100));
id(battery_percent) = percent;
ESP_LOGI("battery", "Raw ADC: %.4f", raw_adc);
ESP_LOGI("battery", "Calculated Voltage: %.2f V", voltage);
ESP_LOGI("battery", "Battery Percent: %d %%", percent);
return voltage;
button:
- platform: restart
name: "Restart device"
font:
- file: "arial.ttf"
id: my_font
size: 11
globals:
- id: rssi_value
type: float
initial_value: '-210.0'
- id: snr_value
type: float
initial_value: '-20.0'
- id: last_tx
type: bool
restore_value: no
initial_value: "false"
- id: last_rx
type: bool
restore_value: no
initial_value: "false"
- id: battery_percent
type: int
initial_value: '0'
display:
- platform: ssd1306_i2c
model: "SSD1306 128x64"
i2c_id: oled_i2c_bus
address: 0x3C
reset_pin: GPIO21
rotation: 0
update_interval: 1s
lambda: |-
// RSSI / SNR
it.print(0, 0, id(my_font), "RSSI:");
it.printf(50, 0, id(my_font), "%.1f dBm", id(rssi_value));
it.print(0, 11, id(my_font), "SNR:");
it.printf(50, 11, id(my_font), "%.1f dB", id(snr_value));
// Temp / Humidity
it.print(0, 22, id(my_font), "Temp:");
it.printf(50, 22, id(my_font), "%.1f °C", id(esp32_lora_sender_01_temperature).state);
it.print(0, 33, id(my_font), "Hum:");
it.printf(50, 33, id(my_font), "%.1f %%", id(esp32_lora_sender_01_humidity).state);
it.print(0, 44, id(my_font), "Battery:");
it.printf(50, 44, id(my_font), "%d %%", id(battery_percent));
Receiver Heltec Lora V4 (I’ve tried V3 also, same result):
substitutions:
name_r02: esp32-lora-receiver-05
friendly_name: ESP32 Lora Receiver 05
esphome:
name: ${name_r02}
name_add_mac_suffix: false
friendly_name: ${friendly_name}
on_boot:
priority: 1000
then:
- output.turn_on: vext_power
- delay: 500ms
esp32:
board: esp32-s3-devkitc-1
framework:
type: esp-idf
# Enable Home Assistant API
api:
encryption:
key: !secret esp32-lora-receiver-05-encryption-key
ota:
- platform: esphome
password: !secret esp32-lora-receiver-05-ota-password
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot (captive portal) in case wifi connection fails
ap:
ssid: "Esp32-Lora-Receiver-01"
password: !secret esp32-lora-receiver-05-ap-password
logger:
hardware_uart: UART0
level: debug
captive_portal:
# SPI for SX126x LoRa
spi:
clk_pin: GPIO9
mosi_pin: GPIO10
miso_pin: GPIO11
sx126x:
cs_pin: GPIO8
rst_pin: GPIO12
busy_pin: GPIO13
dio1_pin: GPIO14
rf_switch: true
rx_start: true
id: lora
hw_version: sx1262
modulation: LORA
frequency: 868800000
crc_enable: true
tcxo_voltage: 1_8V
tcxo_delay: 30ms
sync_value: [0x14, 0x24]
spreading_factor: 9 # Higher SF = better range and SNR
pa_power: 22 # Max out transmit power
bandwidth: 125_0kHz # Narrower bandwidth = better sensitivity
coding_rate: CR_4_5 # Stronger error correction
preamble_size: 12
on_packet:
then:
- lambda: |-
id(rssi_value) = rssi;
id(snr_value) = snr;
id(lora_rssi_sensor).update();
id(lora_snr_sensor).update();
id(last_packet_time) = (int)(millis() / 1000); // record packet time
ESP_LOGI("LoRa", "RSSI: %.2f dBm", rssi);
ESP_LOGI("LoRa", "SNR: %.2f dB", snr);
# ESP_LOGI("LoRa", "Raw hex: %s", format_hex(x).c_str());
packet_transport:
platform: sx126x
update_interval: 10s
providers:
- name: esp32-lora-sender-01
# encryption: !secret encryption_key # if you use encryption
# ping_pong_enable: true
# rolling_code_enable: true
sensor:
- platform: packet_transport
provider: esp32-lora-sender-01
name: "Temperature"
id: esp32_lora_sender_01_temperature
internal: false
unit_of_measurement: "°C"
accuracy_decimals: 2
filters:
- heartbeat: 30s
on_value:
then:
- lambda: |-
ESP_LOGI("LoRaSensor", "📡 New temperature value received!");
ESP_LOGI("LoRaSensor", "Remote ID: esp32_lora_sender_01_temperature");
ESP_LOGI("LoRaSensor", "Temperature: %.2f °C", x);
- platform: packet_transport
provider: esp32-lora-sender-01
name: "Humidity"
id: esp32_lora_sender_01_humidity
internal: false
unit_of_measurement: "%"
accuracy_decimals: 2
- platform: packet_transport
provider: esp32-lora-sender-01
name: "Sender RSSI"
id: esp32_lora_sender_01_rssi
internal: false
unit_of_measurement: "dBm"
accuracy_decimals: 1
- platform: packet_transport
provider: esp32-lora-sender-01
name: "Sender SNR"
id: esp32_lora_sender_01_snr
internal: false
unit_of_measurement: "dB"
accuracy_decimals: 2
- platform: template
name: "LoRa RSSI"
id: lora_rssi_sensor
unit_of_measurement: "dBm"
accuracy_decimals: 1
update_interval: never
lambda: |-
return id(rssi_value);
- platform: template
name: "LoRa SNR"
id: lora_snr_sensor
unit_of_measurement: "dB"
accuracy_decimals: 2
update_interval: never
lambda: |-
return id(snr_value);
button:
- platform: restart
name: "Restart device"
output:
- platform: gpio
pin:
number: GPIO36
inverted: false
id: vext_power
globals:
- id: rssi_value
type: float
initial_value: '-200.0'
- id: snr_value
type: float
initial_value: '-10.0'
- id: last_packet_time
type: int
initial_value: '0'
interval:
- interval: 10s
then:
- lambda: |-
// Get current uptime in seconds
int now_sec = (int)(millis() / 1000);
// If more than 30 seconds since last packet, force RX
if (now_sec - id(last_packet_time) > 30) {
ESP_LOGW("LoRa", "No packet in >30s, forcing RX mode");
id(lora).set_mode_rx();
}
i2c:
- id: oled_i2c_bus
sda: GPIO17
scl: GPIO18
scan: true
font:
- file: "arial.ttf"
id: my_font
size: 10
display:
- platform: ssd1306_i2c
model: "SSD1306 128x64"
i2c_id: oled_i2c_bus
reset_pin: GPIO21
address: 0x3C
rotation: 0
update_interval: 1s
lambda: |-
it.printf(0, 0, id(my_font), "RSSI: %.1f dBm", id(rssi_value));
it.printf(0, 12, id(my_font), "SNR: %.1f dB", id(snr_value));
it.printf(0, 24, id(my_font), "Temp: %.1f C", id(esp32_lora_sender_01_temperature).state);
it.printf(0, 36, id(my_font), "Hum: %.1f %%", id(esp32_lora_sender_01_humidity).state);
it.printf(0, 48, id(my_font), "Last RX: %ds ago",
(int)((millis()/1000) - id(last_packet_time)));
I’m sure you’ll find a lot of things that can be tweaked in a different way, but for now my issue after live testing and walking away from where the receiver is I get maybe 100 meter distance of receiving/sending.
Anyone up for the challenge and can tell me where I have configured my LORA/sx1262 wrong?