In order to test the anemometer input into the wind sensor interface unit I designed, I need to rotate the anemometer shaft at a controlled and accurate rotational speed.

This post explains the hardware, electronics and the firmware used to create a motorised test rig for this purpose.

Measuring wind speed in a specific location usually requires an anemometer to be installed at the location over a period of time. The data can then be averaged to see if the site has a high average wind speed, making it more suitable for the installation of a wind turbine. There are many other reasons to measure wind speed, including for meteorological measurements.

One form of anemometer is the cup anemometer and these are typically the lowest cost. I have already designed and produced an interface which takes the pulses from an anemometer and returns the data through a serial interface. This can be used, along with a wind vane, to give wind speed and direction data. Full design details of the Wind Sensor Interface are available in the github  repository here: https://github.com/curiouselectric/WindSensor and an overview of the Wind Sensor Interface can be found here: https://www.re-innovation.co.uk/docs/anemometer-monitor-for-wind-measurements/.

I needed to test this interface circuit. One way I could do that was running the interface with and anemometer alongside an additional calibrated anemometer unit with a data logger and checking the wind speed is the same. But this would require an outdoor test rig, additional datalogging facilities and calibrated anemometers. It would also take time to get enough data to check a wide range of wind speeds.

So I built a relatively simple test rig with a geared motor to turn the anemometer at a controlled and accurate rate. I can then set the anemometer spinning at a known rate and check that the output from the Wind Sensor Interface matches that data (or investigate why not…). This test rig should also be able to produce a wind speed to anemometer output function, if that is not know for a particular anemometer.

The test rig is shown here:

Here is a video of the rig in action:

There are three main sections:

1 Mechanics

This includes the geared motor and anemometer spinning mechanism. This was mainly built from scrap and bits of wood I had lying around. It holds the anemometer on the same axis as the geared motor. A laser cut arm was added to the output shaft of the geared motor. This has a range of holes so that different anemometers can be fixed and tested. There are two arms so that the anemometer cup does not bounce back from the motor when run at slower speeds (which happened in my first version).

The geared motor also has an encoder on the motor spindle. This provides pulses which means we can know the rotational speed of the motor spindle and hence the rotational speed of the output shaft, if we know the gearing ratio.

The geared motor with an encoder was a DC 12V 250rpm version with part number 25GA370 (from: https://www.ebay.co.uk/itm/185260189825).

2 Electronics

The test rig has a L298N Dual Bridge Motor controller (just something I had left from another project) to power the motor. This is controlled via pulses from an Arduino Uno. The Uno also measures the encoder output pulses.

The wiring information for the encoder on the geared motor is:

  • Red: Motor power +
  • White: Motor power –
  • Black: Coding power- negative
  • Green: Signal feedback (motor rotate 1 circle 11 signal)
  • Yellow: Signal feedback (motor rotate 1 circle 11 signal)
  • Blue: Coding power + Positive

So the Motor + and Motor – go to one output of the L298N motor controller. The L298N is powered from a 12V DC power supply. Three control wires go from the L298N motor controller to the Uno, these are Enable, In1 and In2. Enable controls when power is applied and can be pulsed with a PWM signal to control the speed of the motor. In1 and In2 just control the direct of the motor. I just set In1 High and In2 Low, which meant the motor span in one direction. (I might try changing this in the future for more dynamic testing.)

I also connected the output pulses from the encoder back to the Uno. I wanted to count the pulses from these pins to know the rotational speed of the motor.

ItemUno Pin
L298N EnableD6 (needs PWM available)
L298N In1D5
L298N In2D4
Encoder Black (negative)GND
Encoder Green (pulse signal 0)D2 (for Interrupt)
Encoder Yellow (pulse signal 1)D3 (for Interrupt)
Encoder Blue (positive)+5V
Wiring list

3 Software

The last thing to do was to write software to control the anemometer speed. I wanted this very simple so implemented the following functions:

  • The speed setpoint was set using a serial input. Whatever number you send will become the new speed setpoint, as long as it is within range (set in code).
  • The speed of the motor is controlled by a PWM output.
  • The encoder pulses are counted and averaged every second.
  • The actual speed of the motor is calculated from the encoder pulses. We know that one motor spindle rotation is 11 encoder pulses and I measured that it takes 34 motor spindle rotations to give one output shaft rotation. We also know the time between each encoder pulse measurement. So we calculate the rotations from the encoder pulses divided by 11 x 34 = 374 and we calculate the time from the millis() function. This can be used to calculate the real RPM of the output shaft.
  • The PWM output is adjusted by a PID control algorithm, using the Speed Setpoint and the real RPM as the two parameters we are trying to align.
  • The PWM output is increased if the real RPM is lower than the setpoint and vice versa.
  • To ensure accurate control a PID (Proportional-Integral-Derivative) control algorithm is used. I needed to adjust the three PID factors slightly, but got it to very accurately control the speed.

The code was a quick and relatively dirty hack, but it worked pretty well!

The final Arduino code is given here:

/*  Anemometer Test Rig Code
    By Matt Little  21/9/2023

    This controls a motor to a set speed in order to rotate an anemometer
    An anemometer gives out pulses/signal that relates to wind speed
    This unit is to check the output aligns with the speed and to test linearity
    of anemometer.
    Designed to test my wind sensor unit at low output speeds.

    Design uses an L298N Dual Bridge Motor controller:
    https://lastminuteengineers.com/l298n-dc-stepper-driver-arduino-tutorial/

    It also uses a 25GA370 DC 12V 250rpm geared motor with encoder
    https://www.ebay.co.uk/itm/185260189825
    Encoder wiring info:
    Red: Motor power + (exchange can control rotating and reversing of motor)
    Black: Coding power- negative (polarity cannot be wrong 3.3-5V)
    Green: Signal feedback (motor rotate 1 circle 11 signal)
    Yellow: Signal feedback (motor rotate 1 circle 11 signal)
    Blue: Coding power + Positive (polarity cannot be wrong 3.3-5V)
    White: Motor power - (exchange can control rotating and reversing of motor)
    So motor rotating 1 times = 11 pulses on encoder.
    No-load rotating speed at 12V = 250rpm.

    So output shaft rotating 1 time = 34 motor times = 374 encoder pulses??

*/

// ******** This is for Scheduling Tasks **************************
// Must include this library from Arduino IDE Library Manager
// https://github.com/arkhipenko/TaskScheduler
#include <TaskScheduler.h>
#define _TASK_SLEEP_ON_IDLE_RUN    // Enable 1 ms SLEEP_IDLE powerdowns between tasks if no callback methods were invoked during the pass
// Callback methods prototypes
void t1SCallback();
Task t1S    (TASK_SECOND, TASK_FOREVER, &t1SCallback);
Scheduler runner;

#define enA   6
#define in1   5
#define in2   4

#define FREQ_DELAY      5       // debounce delay time in Seconds 
#define FREQ_PIN_0      2
#define FREQ_PIN_1      3
#define MIN_PWM         55

unsigned int pulse_counter_0 = 0;
unsigned int pulse_counter_1 = 0;
long int     freq_adjust_time = millis();
long int     start_time;

#define RPM_CONV    374      // Conversion factor from pulse count to shaft rpm    
double      shaftRPM = 0.0;
double      pwmOutput = 0;
double      rpmSetpoint = 0;

#include <PID_v1.h>
//Specify the links and initial tuning parameters
double Kp = 0.3, Ki = 0.2, Kd = 0.1;
PID myPID(&shaftRPM, &pwmOutput, &rpmSetpoint, Kp, Ki, Kd, DIRECT);

// *********** These are global variables ***************************
String        inputString = "";           // a String to hold incoming data
bool          stringComplete = false;     // Checks if the string is complete

void t1SCallback() {
  if (!t1S.isFirstIteration())
  {
    // Calculate shaft RPM from Pulses:
     shaftRPM = ((double)pulse_counter_0 / (double)RPM_CONV) / (double)((millis() - start_time) / 60000.0);

    start_time = millis();  // Reset the start time.
    Serial.print(F("PWM: "));
    Serial.print(pwmOutput);
    Serial.print(F("\tP0: "));
    Serial.print(pulse_counter_0);
    Serial.print(F("\tP1: "));
    Serial.print(pulse_counter_1);
    Serial.print(F("\tRPM: "));
    Serial.println(shaftRPM);
    pulse_counter_0 = 0;
    pulse_counter_1 = 0;

  }
}

//ISR Functions for pulse counting
void ISR_PULSE_0()
{
  pulse_counter_0++;    // Increment the counter
}

//ISR Functions for pulse counting
void ISR_PULSE_1()
{
  pulse_counter_1++;    // Increment the counter
}

void setup() {
  // initialize serial communication:
  Serial.begin(9600);
  // reserve 50 bytes for the Strings:
  inputString.reserve(50);

  Serial.println(F("Anemometer Test Start"));
  // Set up motor:
  pinMode(enA, OUTPUT);
  pinMode(in1, OUTPUT);
  pinMode(in2, OUTPUT);
  // Set initial rotation direction
  digitalWrite(in1, HIGH);
  digitalWrite(in2, LOW);

  // Sort out ISR for pulse counting
  attachInterrupt(digitalPinToInterrupt(FREQ_PIN_0), ISR_PULSE_0, FALLING);
  attachInterrupt(digitalPinToInterrupt(FREQ_PIN_1), ISR_PULSE_1, FALLING);

  // Set up Scheduler:
  runner.init();
  runner.addTask(t1S);
  t1S.enable();

  // Set PWM
  analogWrite(enA, pwmOutput); // Send PWM signal to L298N Enable pin
  start_time = millis();

  //turn the PID on
  myPID.SetMode(AUTOMATIC);

}

void loop()
{
  // Deal with schedules tasks
  runner.execute();

  myPID.Compute();
  analogWrite(enA, pwmOutput); // Send PWM signal to L298N Enable pin

  //  if ( millis() > freq_adjust_time + (FREQ_DELAY * 1000))
  //  {
  //    pwmOutput = pwmOutput + 5;
  //    if (pwmOutput > 255)
  //    {
  //      pwmOutput = MIN_PWM;
  //    }
  //    analogWrite(enA, pwmOutput); // Send PWM signal to L298N Enable pin
  //    freq_adjust_time = millis();
  //  }

  // Check for serial data
  if (stringComplete == true)
  {
    // Convert the inputString to the rpmSetpoint
    if (inputString.toDouble() < 20)
    {
      Serial.print(inputString.toDouble());
      Serial.println("\t TOO LOW!");
    }
    else if (inputString.toDouble() > 250)
    {
      Serial.print(inputString.toDouble());
      Serial.println("\t TOO HIGH!");
    }
    else
    {
      rpmSetpoint = inputString.toDouble();
      Serial.print("RPM Setpoint:\t");
      Serial.println(rpmSetpoint);
    }
    inputString = "";
    stringComplete = false;
  }
}

/*
  SerialEvent occurs whenever a new data comes in the hardware serial RX. This
  routine is run between each time loop() runs, so using delay inside loop can
  delay response. Multiple bytes of data may be available.
*/
void serialEvent()
{
  while (Serial.available()) {
    // get the new byte:
    char inChar = (char)Serial.read();
    // add it to the inputString:
    inputString += inChar;
    // if the incoming character is a newline, set a flag so the main loop can
    // do something about it:
    if (inChar == '\n') {
      stringComplete = true;
    }
  }
}

Leave a Reply

Your email address will not be published. Required fields are marked *