Adding a LoRa Radio Interface to the Digi-Key Weather Platform Wind Speed Anemometer


This project covers the process of developing and installing LoRa radio hardware to interface with the #40R anemometer (part number: APRS6504) mounted on the Digi-Key Weather Platform. The LoRa radio interface was developed using a Seeeduino Xiao, Xiao Grove expansion board and a Grove LoRa-E5 radio board.
The project also covers adding the sensor node to an existing ChirpStack based private LoRaWAN network and Machinechat’s JEDI Pro IoT software platform. The LoRaWAN network uses Seeed’s IP67 rated industrial LoRaWAN gateway to forward the LoRa sensor packets to a private ChirpStack LoRaWAN network server running on a Seeed’s ReServer (with Ubuntu linux installed). Machinechat’s JEDI Pro IoT platform runs on the same ReServer.



  • JEDI Pro or JEDI Pro SSE
    Adaptable software for IoT data collection, visualization, monitoring and data storage that can be integrated into IoT solutions. Capabilities include: collect data from sensors, devices and machines; build intuitive real-time and historical data and system view dashboards; create rules to monitor and respond to data conditions automatically; receive alert notifications by email and SMS. JEDI Pro SSE is the Seeed Studio Edition version of JEDI Pro that adds a Data Collector for Seeed’s line of SenseCAP LoRaWAN sensors
  • ChirpStack
    The ChirpStack open-source LoRaWAN Network Server stack provides open-source components for LoRaWAN networks. The modular architecture makes it possible to integrate within existing infrastructures.
  • Arduino
    Arduino is an open-source electronics platform based on easy-to-use hardware and software.


This post is a follow-on project that builds on the related TechForum post Set up a private LoRaWAN Sensor Network with Machinechat and Seeed SenseCAP which details setting up a private LoRaWAN IoT sensor network using off the shelf hardware and software available at Digi-Key. Software used in the related project included Machinechat’s JEDI Pro applications software and ChirpStack’s LoRaWAN network server application. Hardware used in the project included a Seeed reServer x86 server and SenseCAP outdoor LoRaWAN gateway. For this post a Seeeduino Xiao, Grove Seeeduino Xiao expansion board, Grove LoRa-E5 board, and a #40R anemometer are set up to report wind speed over a LoRaWAN network.

#40R Anemometer
The #40R anemometer is a popular metrology instrument for measuring wind speed. Wind speed is measured by the cup-type anemometer that rotates four magnets causing a contact to close four times per revolution. The transfer function for converting anemometer rotation in Hz is:
meters/second = (Hz * 0.383) + 0.324
miles/hour = (Hz * 0.857) + 0.725).
The contact closure is monitored via the two wire cable cable attached to the anemometer base.

Digi-Key Weather Platform
Details on the Digi-Key weather platform can be found at link Digi-Key Weather Platform .


For the project, the 5V rail voltage from the solar powered Digi-Key Weather Platform powers the Seeeduino Xiao and the Xiao’s 3V3 output powers the Grove LoRa-E5 board and weather sensor hardware. The Xiao UART is connected to the TX/RX pins of the Grove LoRa-E5 and Xiao input pin 2 is connected to the wind sensor anemometer.
The Xiao and LoRa-E5 boards and related circuitry are wired on a proto board and housed in an outdoor enclosure (Hammod Manufacturing part# RL6225). The +5V power and wind sensor anemometer are attached to the enclosure using M8 panel mount connectors and cable assemblies.
The Arduino application code runs on the Seeeduino Xiao to send AT commands to the LoRa-E5 and monitor input pin 2. The application joins the LoRa network, monitors the input pin to calculate average/maximum wind speed, encodes the data in CayenneLPP format, sends the data over LoRa, delays and loops back for next reading. Below schematic diagram illustrates how the circuit is wired up and implemented (see Scheme-it Project: LoRaE5_Xiao_40R_anemometer).

Once the circuit was wired up and assembled on the proto board (part# SOLDERBREAD#02), it was placed the Hammond enclosure (part# RL6225 ) with the panel-mount M8 cable assemblies.

Set up the Seeeduino Xiao and Grove LoRa-E5 for the #40R Anemometer

1 - Set up Arduino on the Seeeduino Xiao. See link Getting started with Seeeduino Xiao

2 - Install libraries needed for application. Add these libraries thru Arduino’s Library Manager:

3 - Code walkthrough (filename: Xiao_LoraE5CayenneLPP_AnemRunAvg.ino)

Initial setup

 // below code is based on Seeed LoRaE5 example code and modified to NOT use the display on the Seeeduino Xiao expansion board (just uses a Xiao connected to a LoRaE5 
// Grove expansion board. Seeed example code at
//note: all Seeed LoRaE5 Grove boards have example code App key of "2B7E151628AED2A6ABF7158809CF4F3C" so needs to be changed
// this version changes out the DHT11 sensor for Sparkfun weather meter kit for wind speed and direction

// Anemometer measuring code based on below example code:
//More Information at:
//Version 2.0 | 11-NOV-2020
//SBR modifications 2-NOV-2022
//SensorPin is connected to RC pulldown circuit on anemometer switch
//Wind vane functionality removed from this version
//Anemometer used is industry #40R with 4 magnets per rev, windspeed formula is: (Hz * 1.38) + 1.17
//add in Running Average code that uses RunningAverage library
//update sampling times for average wind speed and max wind speed gust

#include <Arduino.h>
#include <CayenneLPP.h>

// this version adds in RunningAverage code
#include "RunningAverage.h"
RunningAverage myRA(10);
int samples = 0;

// anemometer and direction parameters
const int RecordTime = 3; //Define Measuring Time (Seconds)
const int SensorPin = 2;     // the number of the sensorpin
//const int VanePin = 1;      // pin# connected to wind vane
const int ledPin =  13;      // the number of the LED pin
int InterruptCounter;
float WindSpeed;
float WindSpeedAvg;   //average wind speed
float WindSpeedGust; //max wind speed gust

CayenneLPP lpp(51);   //setup Cayenne LPP (low power payload) buffer - per documentation 51 bytes is safe to send
static char recv_buf[512];
static bool is_exist = false;
static bool is_join = false;
static int led = 0;

int buf_size;  //Cayenne LPP buffer payload size
int Pointer;   //pointer used in Cayenne LPP buffer
int Offset = 12;    //offset to where Cayenne LPP payload data starts
int Loop1;       //loop counter in LoRa payload builder
int Loop2;       //loop counter in LoRa payload builder
int Loop3 = 0;       //loop counter in LoRa parameter send

static int at_send_check_response(char *p_ack, int timeout_ms, char *p_cmd, ...)
    int ch;
    int num = 0;
    int index = 0;
    int startMillis = 0;
    va_list args;
    memset(recv_buf, 0, sizeof(recv_buf));
    va_start(args, p_cmd);
    Serial1.printf(p_cmd, args);
    Serial.printf(p_cmd, args);
    startMillis = millis();
    if (p_ack == NULL)
        return 0;
        while (Serial1.available() > 0)
            ch =;
            recv_buf[index++] = ch;
        if (strstr(recv_buf, p_ack) != NULL)
            return 1;
    } while (millis() - startMillis < timeout_ms);
    return 0;

 // LoRa message receive message buffer RSSI and SNR
static void recv_prase(char *p_msg)
    if (p_msg == NULL)
    char *p_start = NULL;
    int data = 0;
    int rssi = 0;
    int snr = 0;
    p_start = strstr(p_msg, "RX");
    if (p_start && (1 == sscanf(p_start, "RX: \"%d\"\r\n", &data)))
        led = !!data;
        if (led)
            digitalWrite(LED_BUILTIN, LOW);
            digitalWrite(LED_BUILTIN, HIGH);
    p_start = strstr(p_msg, "RSSI");
    if (p_start && (1 == sscanf(p_start, "RSSI %d,", &rssi)))
    p_start = strstr(p_msg, "SNR");
    if (p_start && (1 == sscanf(p_start, "SNR %d", &snr)))

void setup(void)
    digitalWrite(LED_BUILTIN, HIGH);

    myRA.clear(); // explicitly start clean with RunningAverage by clearing buffer

    // anemeometer and wind vane setup
    pinMode(SensorPin, INPUT); //use input to count interrupts of anemometer
    //pinMode(VanePin, INPUT); //use input to read analog voltage of wind vane output
    Serial1.begin(9600);                  //UART serial1 connection to LoRaE5
    Serial.print("E5 LORAWAN TEST\r\n");
    if (at_send_check_response("+AT: OK", 100, "AT\r\n"))
        is_exist = true;
        at_send_check_response("+ID: AppEui", 1000, "AT+ID\r\n");
        at_send_check_response("+MODE: LWOTAA", 1000, "AT+MODE=LWOTAA\r\n");
        //at_send_check_response("+DR: EU868", 1000, "AT+DR=EU868\r\n");  //original
        at_send_check_response("+DR: US915", 1000, "AT+DR=US915\r\n"); 
        //at_send_check_response("+CH: NUM", 1000, "AT+CH=NUM,0-2\r\n");  //original
        at_send_check_response("+CH: NUM", 1000, "AT+CH=NUM,8-15,65\r\n"); // configure channels to match chirpstack
        at_send_check_response("+KEY: APPKEY", 1000, "AT+KEY=APPKEY,\"2B7E151628AED2A6ABF7158809CF4F3C\"\r\n");
        at_send_check_response("+CLASS: C", 1000, "AT+CLASS=A\r\n");
        at_send_check_response("+PORT: 8", 1000, "AT+PORT=8\r\n");
        is_join = true;
        is_exist = false;
        Serial.print("No E5 module found.\r\n");

Main loop - determine average and maximum wind speed, encode in CayenneLPP format and send as LoRa payload data

void loop(void)
//void loop()
    // measure windspeed and add to Running Average data table myRA
    //debug code to print out sample# and min, average, max windspeed 
    Serial.print("samples = ");
    Serial.print("MinAvgMax  ");
    Serial.print(myRA.getMin(), 3);
    Serial.print(myRA.getAverage(), 3);
    WindSpeedAvg = myRA.getAverage();
    Serial.println(myRA.getMax(), 3);
    WindSpeedGust = myRA.getMax();

    Serial.print("Windspeed: ");
    Serial.print(" mph ");

    // due to character byte limitatations in LoRa payload buffer, need to alternate between 
    // the two wind parameters when sending out parameter payload so use loop3
    Serial.print("Loop3 = ");  //debug code
    Serial.println(Loop3);    //debug code

    if ((Loop3 == 0) and (samples == 40))
    //when samples = 40, add WindSpeedAvg to LoRa payload
      lpp.addAnalogOutput(3, WindSpeedAvg);  //channel 3, Wind Speed Avg
      Loop3 = 1;
    else if((Loop3 == 1) and (samples == 50))
    //when samples = 50, add WindSpeedGust to LoRa payload
      lpp.addAnalogOutput(4, WindSpeedGust);  //channel 4, Wind Speed Max
      Loop3 = 0;
      Serial.println("sample limits not reached");

  // clear running average data after samples = 51   
  if (samples == 51)
    samples = 0;
    Serial.println("RunningAverage data cleared");

Sensor subroutine

//count anemometer pulses and calculate windspeed
void measure() {
  InterruptCounter = 0;
  attachInterrupt(digitalPinToInterrupt(SensorPin), countup, RISING);
  delay(1000 * RecordTime);
  // windspeed calculation for #40R anemometer: mph = (Hz * 0.857) + 0.724
  WindSpeed = (float)InterruptCounter / ((float)RecordTime * 0.857) + 0.724;
  if (InterruptCounter == 0)
    WindSpeed = 0;

Latest source code for the Xiao_LoraE5CayenneLPP_AnemRunAvg.ino application is on github at below link:

Determine Grove LoRa-E5 board device EUI

In Arduino, compile and upload Xiao_LoraE5CayenneLPP_AnemRunAvg.ino code into Seeeduino Xiao and enable Serial Monitor. Review Serial Monitor output to determine Grove LoRa-E5 device EUI.

1:43:30.128 -> +ID: DevAddr, 32:30:81:F2
11:43:30.174 -> +ID: DevEui, 2C:F7:F1:20:32:30:81:F2
11:43:30.221 -> +ID: AppEui, 80:00:00:00:00:00:00:06

Add the LoRa-E5 based sensor node to the ChirpStack LoRaWAN network server

(note: this project and below steps assume that a ChirpStack based private LoRaWAN network is active and in range of the LoRa-E5 sensor node, if not refer to TechForum post Set up a private LoRaWAN Sensor Network with Machinechat and Seeed SenseCAP)

1 - In ChirpStack, select Device-profile and Create. Name Device-profile “Seeed LoRaE5”, Select “1.0.2” for LoRaWAN MAC version, Select “A” for LoRaWAN Regional Parameters version, Select “Default ADR algorithm” for ADR algorithm, and enter “3600” for Uplink interval. In JOIN(OTAA/ABP) tab, check box for “Device supports OTAA”. In CODEC tab, select “Cayenne LPP” in CODEC dropdown list.

2 - In ChirpStack, select Applications, then select “FarmTest” and then select Create. Enter “LoRaE5wind” for Device name, enter “description” for Device description, enter Device EUI for Grove LoRa-E5 board (determined from above step “Determine Grove LoRa-E5 board device EUI”), enter “STM32WL Sensors” for Device-profile, and select CREATE DEVICE. (note: for initial testing and demonstration you may want to check Disable frame-counter validation box)

3 - Add Application key for Device. Enter Application key “2B7E151628AED2A6ABF7158809CF4F3C” (note: this is default key in LoRa-E5, to change refer to KEY section of LoRa-E5 AT Command Specification) and select SET DEVICE-KEYS.

Set up and test ChirpStack HTTP Integration with JEDI Pro Generic LoRaWAN Custom Data Collector

ChirpStack is modified to add HTTP integration for forwarding LoRaWAN meta and sensor data to a specified IP address. Machinechat’s Generic LoRaWAN Custom Data Collector Plug-in is used for listening to the specified IP address and parsing the LoRaWAN data for review (when debug enabled) and use in the JEDI Pro platform.
(note: The Custom Data Collector consists of two files, lorawan-linux.bin and config.yml that are available from Machinechat at: )

1 - Enable HTTP Integration in ChirpStack.
Select “Add” in ChirpStack Integrations screen.

2 - Configure HTTP Integration
Select “JSON” for Payload marshaler , add IP address (use same IP as in config.yml file) for Endpoint URL , and select ADD INTEGRATION

3 - Copy lorawan-linux.bin and config.yml files to the ~/jedi/plugins directory where JEDI Pro is installed on the Ubuntu Linux Mini-PC. Modify config.yml file to enable debug and specify IP listening address.
(note: if you have previously installed the lorawan-linux.bin and config.yml files for a different sensor, all you need to do is edit config.yml as shown in step 5 to add the info for the average wind speed and maximum wind speed parameters)

4 - Run Custom Plug-In on command line using “./lorawan-linux.bin ./config.yml” on terminal in Ubuntu Linux Mini-PC and monitor output data. Data should look similar to below (note: remember to make lorawan-linux.bin file executable):

5 - Edit config.yml file to map LoRaWAN data to JEDI Pro data parameters and disable debug. For this project example, edit propertyNames so LoRaWAN cSproperty: “analogOutput.3” is mapped to mcProperty: “AvgWindSpeed” and LoRaWAN cSproperty: “analogOutput.4” is mapped to mcProperty: “MaxWindGust”. Disable debugging by setting “setDebug:” to false and save file.

Set up JEDI Pro Custom Data Collector and Data Dashboard

In JEDI Pro, select “Settings” tab, then select “Data Collectors” and select “Add Collector”. (note: if you have previously added the LoRaWAN custom data collector to your JEDI application this step is not necessary)

Configure Collector as shown below. Name Data Collector “LoRaWAN” (or whatever you prefer), select Collector Type as “Custom Plug-In”, select “lorawan-linux.bin” as Plug-In Executable file, enter location of config.yml file (example: “/home/scottr/jedi/plugins/config.yml” for Plug-in Options, select check box for “Run As Background Process and Monitor” , then select “VALIDATE PLUG-IN” to verifiy functionality.

In JEDI Pro, select “Data Dashboards”, then select “+” to add a new chart.

Configure data charts for Max Wind Speed Gust and Average Wind Speed and select “Add” to include in your Data Dashboard(see example below)


The combination of Arduino, Seeeduino Xiao, LoRa-E5 radio, and #40R anemometer provides a flexible robust wind sensor platform that provides average and maximum wind speed data over LoRa. ChirpStack’s HTTP Integration and Machinechat’s Generic LoRaWAN Custom Data Collector are then configured to bring the wind sensor data into JEDI Pro for IoT data collection, visualization, monitoring and data storage. The example code can be easily modified for other sensors as needed.


Wow that’s the first time I’ve seen the reverse engineered version of my employers best selling sensor in hobby circles. We gave exclusive distribution rights for wind energy to another company, NRG Systems, back in the 90s and gave them exclusive use of the #40 designation. One of their customer/competitors, Second Wind Systems, reverse engineered our design before going out of business. I’d wondered who ended up with the Second Wind molds.

Starting in 1968 we built a reputation for low cost very long life sensors (10 to 20 years in the field, 5 year warranty), So we discontinued our reed switch version in the 1980s since they failed in under 5 years, often after only 2 years or less in high wind areas. That’s when I introduced our Hall Effect version for those who couldn’t cope with a millivolt level AC output over long wires.

For wind Energy use, to get the #40 you must buy from NRG Systems, their wind industry value added services and world class support are essential for wind energy projects.
NRG 40C pickup coil
NRG 40H Hall Effect switch
NRG also offers higher accuracy and special purpose sensors in their full anemometer line.

For non-wind energy usage you can buy from us but be aware we do not have a lot of manpower for support of our sensors only (support staff is mostly for complete instrument packages).
Our current wind sensor selection, the #400 is like the #40c but without the wind tunnel calibration.

1 Like