Small update:
I have used heltec’s sample code to have 2 of my boards to send and receive. And I successfully used ChatGPT to edit the code so it transmits when I push a button. Pretty cool, and yes I expect I’ll get some hate for taking such an obvious shortcut as AI to code for me. But if anything it’s accelerating my understanding of what I need to know right now.
Next step will be integrating the anemometers, when they eventually arrive!!
#include "LoRaWan_APP.h" // Heltec's LoRaWAN/Radio abstraction library
#include "Arduino.h" // Standard Arduino core library for ESP32
// ==========================
// ===== LoRa Settings ======
// ==========================
// Frequency band (Europe = 868 MHz)
#define RF_FREQUENCY 868000000 // Hz
// Radio transmit power in dBm (max 20, but 5 is fine for testing)
#define TX_OUTPUT_POWER 5
// Bandwidth options: 0=125kHz, 1=250kHz, 2=500kHz
#define LORA_BANDWIDTH 0
// Spreading factor controls range vs speed (SF7 = faster, shorter range)
#define LORA_SPREADING_FACTOR 7
// Coding rate: 1=4/5 (best balance), 4=4/8 (most robust, slowest)
#define LORA_CODINGRATE 1
// Preamble tells receiver a packet is coming — 8 symbols is standard
#define LORA_PREAMBLE_LENGTH 8
// Timeout for LoRa symbols (not usually needed for TX)
#define LORA_SYMBOL_TIMEOUT 0
// Packet settings — most examples leave these as-is
#define LORA_FIX_LENGTH_PAYLOAD_ON false
#define LORA_IQ_INVERSION_ON false
// Timeout for receive mode (not used much here)
#define RX_TIMEOUT_VALUE 1000
// Max number of bytes per packet
#define BUFFER_SIZE 30
// ==========================
// ===== Button Settings ====
// ==========================
// GPIO pin the pushbutton is connected to
#define BUTTON_PIN 47 // Connect button between GPIO47 and GND
// ==========================
// ===== Global Variables ====
// ==========================
// Character array to hold the message text
char txpacket[BUFFER_SIZE];
// A counter to track how many packets have been sent
double txNumber = 0;
// Flag to indicate whether the radio is currently idle or busy
bool lora_idle = true;
// Struct required by Heltec’s library to link radio event handlers
static RadioEvents_t RadioEvents;
// ==========================
// ===== Event Handlers =====
// ==========================
// Called automatically when a packet finishes transmitting
void OnTxDone(void);
// Called if transmission fails or times out
void OnTxTimeout(void);
// ==========================
// ===== Setup Function =====
// ==========================
void setup() {
Serial.begin(115200); // Start serial console for debugging
Mcu.begin(HELTEC_BOARD, SLOW_CLK_TPYE); // Initialize Heltec board hardware
// --- Setup LoRa radio ---
// Assign your callback functions to the Radio event handlers
RadioEvents.TxDone = OnTxDone;
RadioEvents.TxTimeout = OnTxTimeout;
// Initialize the LoRa radio hardware
Radio.Init(&RadioEvents);
// Set the channel frequency (must match receiver)
Radio.SetChannel(RF_FREQUENCY);
// Configure LoRa transmission parameters
Radio.SetTxConfig(MODEM_LORA, // LoRa modulation
TX_OUTPUT_POWER, // Output power
0, // Frequency deviation (not used in LoRa)
LORA_BANDWIDTH, // Bandwidth (125kHz)
LORA_SPREADING_FACTOR, // SF7
LORA_CODINGRATE, // Coding rate 4/5
LORA_PREAMBLE_LENGTH,// Preamble
LORA_FIX_LENGTH_PAYLOAD_ON, // Dynamic packet length
true, // CRC enabled
0, 0, // Frequency hopping (off)
LORA_IQ_INVERSION_ON,// IQ setting (false = standard)
3000); // Transmission timeout (ms)
// --- Setup Button ---
pinMode(BUTTON_PIN, INPUT_PULLUP);
// INPUT_PULLUP keeps the pin HIGH by default,
// and reads LOW when the button is pressed (to GND)
Serial.println("Sender ready. Press button to send LoRa packet.");
}
// ==========================
// ===== Main Loop ==========
// ==========================
void loop() {
// This processes any LoRa interrupt events such as TX done/timeouts
Radio.IrqProcess();
// Check if button is pressed (LOW = pressed due to pull-up)
if (digitalRead(BUTTON_PIN) == LOW && lora_idle) {
delay(50); // Debounce delay to avoid false triggers
// Confirm it's still pressed after debounce delay
if (digitalRead(BUTTON_PIN) == LOW) {
// Increment packet counter
txNumber += 1;
// Prepare the message text
sprintf(txpacket, "Hello world number %.0f", txNumber);
// Print to serial monitor
Serial.printf("\r\nSending packet: \"%s\" (%d bytes)\r\n", txpacket, strlen(txpacket));
// Send the packet over LoRa
Radio.Send((uint8_t *)txpacket, strlen(txpacket));
// Mark radio as busy to prevent duplicate sends
lora_idle = false;
// Wait until button is released before allowing another send
while (digitalRead(BUTTON_PIN) == LOW) {
delay(10);
}
}
}
}
// ==========================
// ===== Event Handlers =====
// ==========================
// Called when the radio finishes sending successfully
void OnTxDone(void) {
Serial.println("TX done...");
lora_idle = true; // Ready to send again
}
// Called when the transmission times out or fails
void OnTxTimeout(void) {
Radio.Sleep(); // Put radio into low power mode
Serial.println("TX Timeout...");
lora_idle = true;
}



