DIY LoRa-based Anemometer Network Using ESP32 – Need Guidance on: solar, Heltec WiFi LoRa 32, RS485 & LoRa Setup

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

Please edit your post to add code tags. Select the code and push the post editor <code> button.

apologies, now done!

Hey all so I have planned out the wiring for this - super grateful for a second pair of eyes on this. Sorry ive not made a diagram.

Components:

Wiring:

  • Anemometer (yellow) goes to RS485 A
  • Anemometer (blue) goes to RS485 B
  • Anemometer (brown) goes to DC boost board: 12V DC+out
  • Anemometer (black) goes to DC boost board GND: 12V DC-out
  • Heltec 3V3 pin goes to boost board DC+in (utilising an inline 200mA fuse)
  • Heltec GND pin goes to the boost board DC-in
  • Heltec 3V3 goes to MAX485’s VCC (this powers the MAX485)
  • Heltec GND to MAX485’s GND (returns the 3V)
  • Heltec PIN#? To MAX485's RXD (send data to the MAX485, GPIO PIN number TBC)
  • Heltec PIN#? To TXD (rx data from MAX485, GPIO PIN number TBC)

Does this look correct? I've referred to a guide linked below but have had to adapt to my board and the need for a DC-DC boost board.

Thanks!
Nick

Guide: Measure Wind Speed with Ultrasonic Anemometer & Arduino

Have you checked that the load of everything you are powering off the Heltec’s 3.3V pin does not exceed the capacity of its onboard 3.3V regulator?

You might also want to use a bigger battery for each node. Since the nodes will be transmitting pretty often the power consumption will be high. I did everything possible on my water meter nodes to reduce consumption and I still have to recharge the 3.7V Lipos every few weeks. That gets very boring very quickly. I wish I had used a bigger battery but I have no room with my current design.

Hi thanks. So I have since discovered that I will need to run a wire directly from the battery positive to the DC-DC boost board. The anemometer draws 0.12W so is quite low. However, I’ve no need to keep the node physically small so I can increase battery size in future if needs be.

I am just prototyping at the moment. And in any case, will add on solar so that the batteries remain topped up.

I hope your project is still doing you well!

What is your real goal? Are you studying wind turbulence? If not, how far away from wind obstructions will you place the anemometers?

I ask because I have tried to get an anemometer based on an Arduino nano to register reasonable wind speed for several years. The spinning sensor is about 3 feet above a patio roof. Indicated speeds are way high.
I recently purchased a commercial weather sensor and display with remote transmission. It is placed on a 6 ft. post very far from any wind obstruction and reports very reasonable wind speeds.
The home made device so near to the house reports up to 10 times the correct wind speed, all due to turbulence.

great news, I have created a working prototype!! I tested LoRa separately but this prototype confirms the concept of the node will work and report the wind reading into the terminal output window. Exciting times folks, thanks for your help so far!


For the sake of documenting my progress, this is my crudely drawn Johnny-age-5 circuit diagram showing

  • Heltec v3 LoRa board at the top
  • RS485 chip in the middle
  • 12v step up chip at the bottom
  • Top left: battery and LoRa antenna
  • Right hand side: power switch, the cable to the anemometer and below it the usb-c port which is just connected to the usb-c on the heltec

I’ve already built the base station on perfboard so I’m pretty excited to construct this node too. This is the first time I’ve ever done anything like this.

Code - all my code works when using breadboard and using the terminal window in my arduino environment. So hopefully smashing all this tech into a prototype box won’t actually change anything except make it ready to test in the wild!!

Well it works! Today I ran the node and base station out for a field test. On “default” settings (thanks ChatGPT), it managed 300 metres.

I’m acquiring larger antennae before I start titting about with Spread Factor and Power.