Timers and Interrupts: Misfiring

@ScruffR @peekay123
Good day Particle Community

I am having some challenges measuring wind speed accurately. Im using a Davis anemometer which is essentially a reed switch closed by a magnet (for wind speed) and a potentiometer (Wind direction). I have used a debounce circuit like the one attached.

When i run the following code, the interrupt i used seems to run perfectly both with and without Blynk communication

//Davis Anamometer Windspeed 
int DigitalPin1=D3; //pin used to read pulses for windspeed 
void WindSpeed(void); //Timer interrupt
void Revolution(void); //Interrupt function ISR
float SampleTime=5000; //Sample time in milliseconds
float Speed; //Wind speed

volatile int RevCount=0; //Since the main program and ISR are sharing this variable

Timer timer(SampleTime, WindSpeed);//Use of timers to calculate Windspeed more precise timing than milli and it is behaves like an interrupt

void setup() {
    
    Serial.begin(9600);
    timer.start();
    
    pinMode(DigitalPin1,INPUT_PULLDOWN);
    attachInterrupt(DigitalPin1, Revolution, FALLING);  //When the reed switch closes, ISR triggered on the Falling edge only removing false reading 
    Serial.println("Windspeed test");
    delay(2000);


}

void loop() {
    
    Serial.printlnf("");
    Serial.printlnf("RevCount= %d", RevCount);
    //Blynk.virtualWrite(V5, Direction); //Replace serial code with blynk but put here in loop Function
    delay(300);
   
}

 void Revolution(){

    //Hardware solves debounce issue
    RevCount=RevCount+1;   
    
 }

 void WindSpeed(){
     
    Serial.printlnf("WindSpeed every %d milliseconds", millis()); 
    Speed=(3.621015*RevCount)/(SampleTime/1000); //Speed in km/hr. Fromula adpated from technical manual check Bookmarks Davis says 1600 rev/hr ---> 1 mile/hour
    Serial.printlnf("Wind speed %f km/hr", Speed);
    Serial.printlnf("RevCount %d", RevCount);
    RevCount=0;
    timer.reset(); //RESET THE TIMER TO RESTART THE COUNT FROM ZERO TILL IT REACHES 5
    
 }

However when i combine the operation of the wind speed, wind direction and a few other sensors than my interrupts seems to occur more than in should, which is indicative of the arbitrary rev count that shows up on the serial monitor, i.e the rev count will be 7 and suddenly jump to 92. With the Blynk communication code commented out or not i still get false measurements. Here is the code for the added sensors.


#include <CE_BME280.h>
#include <BlynkSimpleSerialBLE.h>
#include<Wire.h>                                            //Is preinstalled on the particle IDE    https://community.particle.io/t/porting-from-arduino-to-particle-io-simplified-hopefully/20072
//#include<Particle.h>                                      //Does not seem to be required. Only required if arduino program requires arduino.h or application.h    https://community.particle.io/t/porting-from-arduino-to-particle-io-simplified-hopefully/20072
#define SEALEVELPRESSURE_HPA (1013.25)                     
#define Addr 0x76                                           //BME280 address 118
#define Addr1 0x39                                          //TSL2561 address  57
#define Addr2 0x4A                                          //Max4409 addres 74


//char auth[] = "AUTH TOKKEN 1";         //For Bluetooth Classic
char auth[] = "AUTH TOKKEN 2";           //Bluetooth BLE

void WindsensorSetup(void);                                 //Function to initialse the windsensor
void TSL2561LuxSensorSetup(void);                           //TSL2561 Sensor Setup
void Max44009LuxSensorSetup(void);                          //Max44009 Sensor data
void GetBME280SensorData(void);                             //Get BME280 Sensor data
void GetTSL2561SensorData(void);                            //Get TSL2561 Sensor data
void GetMax44009SensorData(void);                           //Get Max44009 Sensor data
void Revolution(void);                                      //Interrupt function ISR
void WindDirection(void);                                   //To get wind direction data
void BlynkComms(void);                                      //To send weather data to the Blynk application
void DisplayOnSerialMonitor(void);                         

float Temperature;
float Pressure;
float pressure1 = 991;
float pressure2 = 991;
float Humidity;
float Altitude;
float Dewpoint;
byte BME280SensorConnected=0;                               
String BME280SensorState;                                   //To store the connection status of the sensor

int WindspeedPin=D3;                                         //Digital Read pin to detect revolutions of wind speed sensor 
int AnalogPin1=A1;                                          //Analog read pin for Wind Direction
float AnalogValue=0;                                        //Voltage of the potentiometer from the wind direction sensor
float SampleTime=5000;                                      //Sample time in milliseconds
float Speed=0;                                              //Wind speed in in Km/hr 
volatile int RevCount=0;                                    //Since the main program and ISR are sharing this variable
byte WindsensorConnected=0;                                 //Variable used to control the execution of the WindsensorSetup function
String WindsensorState;                                     //Store the connection status of the windsensor
String Direction;                                          
String North("North");
String NorthEast("North East");
String East("East"); 
String SouthEast("South East");
String South("South");
String SouthWest("South West");
String West("West");
String NorthWest("North West");

byte LuxSensorResponse;                                     //Stores the response from the sensor to test connection of sensor to the weather station 
double ch0;                                                 //IR + visible light
double ch1;                                                 //IR light 
double LightIntensity;                                  //Visible light


CE_BME280 bme;                                              // I2C

Timer timer(SampleTime, WindSpeed);                       


void setup() {

    Serial.begin(9600);
    //Serial1.begin(9600);                                  //Baudrate compatible with Bluetooth Classic
    Serial1.begin(9600);                                    //Baudrate 115200 is the default but can be changed using AT+BOUD4
    
    Blynk.begin(Serial1, auth);                             //Initilaise communication with Blynk application via bluetooth 
    
    timer.start();                                          //Initialize Timer interrupt
    pinMode(WindspeedPin,INPUT_PULLDOWN);
    attachInterrupt(WindspeedPin, Revolution, FALLING);      //When the reed switch closes, ISR triggered on the FALLING edge only
                                                            //A hardware debounce circuit is applied to prevent reed switch chatter 
    
   
    //BME280 DETECTION
    if(!bme.begin(Addr)){                                   
    
    Serial.println("BME280 Error");
    
    }
    else
    Serial.println("BME280 interfaced with BLYNK test");
    delay(1000);
    
    
    //TSL2561 DETECTION
    TSL2561LuxSensorSetup();                                //Set up of the Lux Sensor
    if(LuxSensorResponse == 0){                             //Detect lux sensor is connected to the weather station
        
    Serial.println("Lux sensor Interfaced with BLYNK test");
   
    }
    else
    Serial.println("TSL2561 Lux sensor Error");
    delay(1000);
    
    
    //WIND SENSOR INITIALIZATION WILL GO HERE
    Serial.println("Windspeed and Wind direction Interfaced with BLYNK test\n");
    delay(1000);
    
}


void loop() {                                               
    
    
    GetBME280SensorData();
   
   //_____________________________________________________________________________________________________________________________+
    if(Speed==0){                                           //if there is a wind speed measurement than look for the wind direction
        
        Direction="None";
        
    }
    else
    WindDirection();                                        //Call wind direction function
  //______________________________________________________________________________________________________________________________+
    
     
  //______________________________________________________________________________________________________________________________-
    GetTSL2561SensorData();                                 //Call light sensor function
  //______________________________________________________________________________________________________________________________-
    BlynkComms();
    DisplayOnSerialMonitor();                               //For testing purposes only
    
}


void TSL2561LuxSensorSetup(){
    
     
// Initialise I2C communication as MASTER 
  Wire.begin();                                             
  
  Wire.beginTransmission(Addr1);          
  LuxSensorResponse = Wire.endTransmission();              //Detect if Lux sensor is connected to weather Station
  
   // Starts I2C communication
  Wire.beginTransmission(Addr1);
  // Select control register
  Wire.write(0x00 | 0x80);
  // Power ON mode
  Wire.write(0x03);
  // Stop I2C Transmission
  Wire.endTransmission();

  // Starts I2C communication
  Wire.beginTransmission(Addr1);
  // Select timing register
  Wire.write(0x01 | 0x80);
  // Nominal integration time = 402ms
  Wire.write(0x02);
  // Stop I2C Transmission and the response from the sensor at the end of this transmission is stored in TSL2561SensorResponse
  Wire.endTransmission();
  
     
       
}


void GetBME280SensorData(){
     
    Temperature = bme.readTemperature();
    Humidity = bme.readHumidity();
    Pressure = (bme.readPressure()/100);                    //To convert to hPa
    Dewpoint = Temperature - ((100 - Humidity)/5);          
     
}
 
   
void Revolution(){

    //Hardware solves debounce issue
    RevCount=RevCount+1;    
   
}


void WindSpeed(){
     
    Speed=(3.621015*RevCount)/(SampleTime/1000);            //Speed in km/hr. Formula adapted from technical manual check Bookmarks Davis says 1600 rev/hr ---> 1 mile/hour
    RevCount=0;
    timer.reset();                                          //RESET THE TIMER TO RESTART THE COUNT FROM ZERO 
    
}
 
 
void WindDirection(){
    
    int RawValue=0;

   
    RawValue=analogRead(AnalogPin1);
    AnalogValue=(3.3*RawValue)/4096;
    
    if(AnalogValue>= 2.8875){
       
        Direction=NorthWest; 
    
    }
    
    else if(AnalogValue>= 2.475){
        
        Direction=West; 
      
    }
    
    else if(AnalogValue>= 2.0625){
        
        Direction=SouthWest; 
       
    }
    
    else if(AnalogValue>= 1.65){
        
        Direction=South; 
 
    }
    
    else if(AnalogValue>= 1.2375){
        
        Direction=SouthEast; 
   
        
    }
    else if(AnalogValue>= 0.825){
        
        Direction=East; 
        
    }
    
    else if(AnalogValue>=0.4125 ){
        
        Direction=NorthEast; 
     
    }
    
    else if(AnalogValue<0.4125 ){
       
        Direction=North; 
        
    }
    
}


void GetTSL2561SensorData(){
    
     unsigned int data[4];
  for(int i = 0; i < 4; i++) 
  {
    // Starts I2C communication
    Wire.beginTransmission(Addr1);
    // Select data register
    Wire.write((140 + i));   
    // Stop I2C Transmission
    Wire.endTransmission();
    
    // Request 1 byte of data
    Wire.requestFrom(Addr1, 1);
    
    // Read 1 bytes of data
    if(Wire.available() == 1)
    {
      data[i] = Wire.read();
     }
     delay(200);
  }
  
  // Convert the data
  ch0 = ((data[1] & 0xFF) * 256) + (data[0] & 0xFF);        //Full spectrum light (Visible + IR))
  ch1 = ((data[3] & 0xFF) * 256) + (data[2] & 0xFF);        //IR spectrum light 
  LightIntensity=ch0-ch1;                                   //Visible light
    
}

 
void BlynkComms(){
     
    Blynk.run();
    Blynk.virtualWrite(V0, Temperature);
    Blynk.virtualWrite(V1, Humidity);
    Blynk.virtualWrite(V2, Pressure);
    Blynk.virtualWrite(V4, Dewpoint);
    Blynk.virtualWrite(V5, Direction); 
    Blynk.virtualWrite(V6, Speed);
    Blynk.virtualWrite(V7, LightIntensity);
    
}  
 
 
 
//DISPLAYS ALL MEASUREMENTS ONTO THE SERIAL MONITOR. COMMENT OUT/REMOVE AFTER TESTING PHASE COMPLETE
void DisplayOnSerialMonitor(){
     
    Serial.print("Temperature ");                           
    Serial.print(Temperature);
    Serial.println(" C");
    Serial.print("Humidity ");
    Serial.print(Humidity);
    Serial.println(" %");
    Serial.print("Pressure ");
    Serial.print(Pressure);
    Serial.println(" hPa");
    Serial.print("Dewpoint ");
    Serial.print(Dewpoint);
    Serial.println(" C");
    Serial.printlnf("RevCount= %d", RevCount);              //Count changed to RevCount
    Serial.printlnf("Voltage8 %fV",AnalogValue);
    Serial.printlnf("Wind speed %f km/hr", Speed);
    Serial.print("Wind direction: ");
    Serial.println(Direction);
    Serial.printlnf("Full Spectrum(IR + Visible) : %f Lux", ch0 );
    Serial.printlnf("Infrared Value : %f Lux", ch1);
    Serial.printlnf("Visible Value : %f Lux", LightIntensity);
    Serial.println("");
    
}

At first i thought there was an issue with the code implemented for Blynk communication but after commenting out Blynk and running the above weather station code i still have the arbitrary jumps in Revcount.
I cant identity which lines of code that could be causing this unwanted behavior. Could it be the mixture of GPIOs that i am using that could be causing the problem? Or is that there is too much happening in my program which is causing false interrupt measurements?
Here is the setup of the sensors that i have connected to my particle photon:

The BME280 sensor is connected via I2C onto pins D0 and D1
The TSL2561 lux sensor is connected via I2C onto pins D0 and D1
The Davis wind speed is connected to D3
The Davis wind direction is connected to A1
Bluetooth BLE Jdy-08 is connected to TX ad RX

Its also worth mentioning that i have used a seperate power supply to test power the sensors and used a common ground to the photon to make everything work.

Additionally i have also tested the wind speed sensor in isolation using both the external supply and photons supply using the two codes above and still i have the issue.
Please can someone advise.

If the wrong reading is always is 92, I’d suspect a buffer overflow somewhere else.
You could try moving the definition of your volatile int RevCount=0; somewhere else.

@ScruffR
Thank you for you response. It actually doesn’t always do that. It actually sometimes skips from 3 to 23 than 36. When I make a single contact it jumps by 10. And this happens randomly but frequently. And this is only the case with the second set of code I uploaded above not the first simpler code. With the first set I don’t have that problem.

To exclude switch bounce from the list of possible causes, you could also implement a crude software debounce like this

const    uint32_t usDebounce = 50000;            // play with that value
volatile uint32_t usLastInt = 0;
void Revolution() {
  if (micros() - usLastInt < usDebounce) return; // ignore repeated triggers within 50ms 
  usLastInt = micros();
  RevCount++;    
}

HW debounce should be work well and good, but double safety shouldn’t harm :wink:

If the errors still occure, play a bit with the debounce timeout and if that doesn’t help at all, you probably have some kind of memory corruption going on, but finding that won’t be easy.

One way could be to implement a check macro for monotonic increment of the value that creates a log output once you detect a jump. If you sprinkle that all over your code you may be able to lacalise the misbehaving code block.

BTW, you may want to consider the ~40k internal pull-down resistor in your debounce circuitry too. Maybe the voltage divider there keeps the level close to the trigger level.
What voltage is +V in your schema?
Do you have the optional diode in place - exactly that type?

I software debounce my anemometer. The anemometer reed switch connects to GND and there’s an external 10K resistor pull-up to 5V.

It’s interrupt-based:

	attachInterrupt(ANEMOMETER_PIN, anemometerInterrupt, FALLING);

with this interrupt hander:

void anemometerInterrupt() {
	if (millis() - lastAnemometerTime >= DEBOUNCE_TIME_MS) {
		lastAnemometerTime = millis();
		anemometerCount++;
	}
}

DEBOUNCE_TIME_MS is 10 milliseconds.

In loop, once per second I aggregate the wind speed in that second, and also measure maximum gust speed:

if (millis() - lastAnemometerAggregationTime >= 1000) {
		lastAnemometerAggregationTime = millis();

		// Process anemometer data once per second
		// Anemometer (D5)
		// One contact closure (falling) = 1.492 MPH (2.4 km/h) of wind speed
		//Log.info("anemometerCount=%d vaneRaw=%.1f", anemometerCount, 22.5 * (float)adcToDirectionIndex(analogRead(VANE_PIN)));

		if (anemometerCount > 0) {
			float speed = 1.492 * (float) anemometerCount;
			anemometerCount = 0;

			if (speed > windGust) {
				windGust = speed;
			}
			windSum += speed;
			windCount++;
		}

	}

And every 6 seconds, if the values change, then the new values are published:

	if (millis() - lastWeatherPublishTime >= PUBLISH_TIME_MS) {
		lastWeatherPublishTime = millis();

		char buf[128];

		if (windCount > 0) {
			snprintf(buf, sizeof(buf), "{\"weatherWind\":%.1f,\"weatherGust\":%.1f}",
					(windSum / (float)windCount), windGust);
			localEventQueue.publish("environment", buf);

			windGust = 0.0;
			windSum = 0.0;
			windCount = 0;
			windWasNonZero = true;
		}
		else
		if (windWasNonZero && windCount == 0) {
			// Was non-zero, but now it's zero
			snprintf(buf, sizeof(buf), "{\"weatherWind\":%.1f,\"weatherGust\":%.1f}",
					0.0, 0.0);
			localEventQueue.publish("environment", buf);
			windWasNonZero = false;
		}

It also does rain and wind direction, but I’ve omitted that code here.

5 Likes

@Alli Do you have a DSO / Digital Storage Oscilloscope? With the value of C and R in your debouncing circuit you will be seeing GPIO pin at +V when SW1 is open and when closed a pretty slow reduction to GND. Is it possible that with different speeds of SW1 opening and closing the GPIO signal is sometimes never crossing the threshold level to LOW? Have you tried C1 as 100nF/0.1uF or a smaller value of R2?

1 Like

Thank you for the advice guys. I will try th software debounce out. But after further testing i have come to the conclusion that my problem is a software issue for sure. I isolated (commented out) the weatherstation code above. First i only ran the windspeed and direction code. No problems there, count occurred realistically (i get consistent counts of the interrupt). But as soon as i include the BME280 and the Lux Sensor i start getting sporadic counts of the interrupt. I even ran the following code and i was able to achieve repeatable results for the wind speed and the counts of the interrupt were consistent:


//Portable Weather Station:WIND SPEED
//Works with debounce circuit
#include <BlynkSimpleSerialBLE.h>

char auth[] = "c39c9a12d53241fca5eb0975bc7c1b41";
void WindSpeed(void); //Timer interrupt
void Revolution(void); // Interrupt function ISR
float SampleTime=5000; //Sample time in milliseconds
float Speed=0; //Wind speed
int DigitalPin1=D3; //pin used to read pulses for windspeed 
int AnalogPin1=A1; //pin used to read analog voltage of wind sensor
String Direction;//The will be no wind direction if wind speed is 0

//SYSTEM_MODE(MANUAL);

volatile int RevCount=0; //Since the main program and ISR are sharing this variable

Timer timer(SampleTime, WindSpeed);//Use of timers to calculate Windspeed more precise timing than milli 
  
 void setup() {

    Serial.begin(9600);
    timer.start(); //Initialize Timer interrupt
    Serial1.begin(9600);
    Blynk.begin(Serial1, auth);
    pinMode(DigitalPin1,INPUT); //D3 as an input pin
    attachInterrupt(DigitalPin1, Revolution, FALLING);  //When the reed switch closes, ISR triggered on the FALLING edge only removing false reading
                                                        
    Serial.println("Windspeed and Wind direction test");
    delay(2000);
    
    }

 void loop() {
     
    Serial.printlnf("");
    Serial.printlnf("RevCount= %d", RevCount);
    
    if(Speed==0){ //if there is a wind speed measurement than look for the wind direction
        
        Direction="None";
        
    }
    else
    WindDirection(); //call wind direction function
    
    Serial.printlnf("Wind speed %f km/hr", Speed);
    Serial.print("Wind direction: ");
    Serial.println(Direction);
    Blynk.virtualWrite(V5, Direction); 
    Blynk.virtualWrite(V6, Speed);
    delay(1000);
 
 }

 void Revolution(){

    //Hardware solves debounce issue
    RevCount=RevCount+1;    
    
 }
 
 void WindSpeed(){
     
    Speed=(3.621015*RevCount)/(SampleTime/1000); //Speed in km/hr. Fromula adpated from technical manual check Bookmarks Davis says 1600 rev/hr ---> 1 mile/hour
    RevCount=0;
    timer.reset(); //RESET THE TIMER TO RESTART THE COUNT FROM ZERO 
    delay(1000);
 }
 
 void WindDirection(){
    
    int RawValue=0;
    float AnalogValue=0;
    String North("North");
    String NorthEast("North East");
    String East("East"); 
    String SouthEast("South East");
    String South("South");
    String SouthWest("South West");
    String West("West");
    String NorthWest("North West");
   
    RawValue=analogRead(AnalogPin1);
    AnalogValue=(3.3*RawValue)/4096;
    
    if(AnalogValue>= 2.8875){
       
        Direction=NorthWest; 
        Serial.printlnf(" Voltage1 %fV", AnalogValue); //Just to test Remove.
       
    }
    
    else if(AnalogValue>= 2.475){
        
        Direction=West; 
        Serial.printlnf("Voltage2 %fV",AnalogValue);    
        
    }
    
    else if(AnalogValue>= 2.0625){
        
        Direction=SouthWest; 
        Serial.printlnf("Voltage3 %fV",AnalogValue);
        
    }
    
    else if(AnalogValue>= 1.65){
        
        Direction=South; 
        Serial.printlnf("Voltage4 %fV",AnalogValue);
    }
    
    else if(AnalogValue>= 1.2375){
        
        Direction=SouthEast; 
        Serial.printlnf("Voltage5 %fV",AnalogValue);
        
    }
    else if(AnalogValue>= 0.825){
        
        Direction=East; 
        Serial.printlnf("Voltage6 %fV",AnalogValue);
        
    }
    
    else if(AnalogValue>=0.4125 ){
        
        Direction=NorthEast; 
        Serial.printlnf("Voltage7 %fV",AnalogValue);
        
    }
    else if(AnalogValue<0.4125 ){
       
        Direction=North; 
        Serial.printlnf("Voltage8 %fV",AnalogValue);
    }
    
 }

@ScruffR @rickkas7
I will try the software debounce and see if that helps. I just dont want to hold the program for too long in the interrupt.
I did consider the 40k pull up. i tested the code with and without the pull up. Same result
3.3V is my voltage
Yes i do. Yes the exact diode.

@armor
I actually do not have an oscilloscope but yes it should behave that way according to theory.
I also thought that this might be the problem, however when i run the code responsible for the windpseed only (interrupt driven code only) than i achieve consistent counts of the interrupt without any sporadic jumps. It seems that additional code is causing some kind of problem. I will try to implement a software debounce and hopefully it solves the problem.

BTW Happy new year Particle community :slightly_smiling_face:

Just some hint on your other code
I’d rewrite your WindDirection() function this way (to avoid use of String and also condense the code a bit).

char Direction[12];
const char directions[][12] = 
{ "None"
, "North"
, "North East"
, "East"
, "South East"
, "South"
, "South West"
, "West"
, "North West"
};
void WindDirection() {
  int direction = 0;
  if (Speed != 0) {
    int RawValue = analogRead(AnalogPin1);
    direction = map(RawValue, 0, 4095, 1, 8);
    double AnalogValue = (3.3 * RawValue) / 4095;
    Serial.printlnf(" Voltage1 %fV, RawValue %d, Direction %s (%d)"
                    , AnalogValue, RawValue, directions[direction], direction);
  }
  strcpy(Direction, directions[direction]);
}

Also you may want to lock the update of RevCount on entry into WindSpeed() (either with a lock-flag or by detaching/blocking the interrupt).

3 Likes

@ScruffR @armor @rickkas7

Thank you very much guys. I have implemented a software debounce in conjunction with my hardware debounce and it seems to have resolved the sporadic behaviour of the interrupt count!

@ScruffR
Thank you. I will try to condense my wind direction code as you advised.

2 Likes

Hello ScruffR

This code is not working as expected. The last direction "North West" only occurs when the voltage is 3.3V exactly. Here is my code:


int AnalogPin1=A1;
float AnalogValue = 0;
int RawValue=0; 
char Direction[12];
const char directions[][12] = 
{ "None"
, "North"
, "North East"
, "East"
, "South East"
, "South"
, "South West"
, "West"
, "North West"
};

 void setup() {
     
 	Serial.begin(9600);
    Serial.println("Wind direction test Version2");
    delay(2000);
    
 }

 void loop() {
    
   WindDirection();
   Serial.println("");
   delay(1000);

 }

void WindDirection() {
  int direction = 0;
 // if (Speed != 0) {
    int RawValue = analogRead(AnalogPin1);
    direction = map(RawValue, 0, 4095, 1, 8);
    AnalogValue = (3.3 * RawValue) / 4095;
    Serial.printlnf(" Voltage1 %fV, RawValue %d, Direction %s (%d)"
                    , AnalogValue, RawValue, directions[direction], direction);
 // }
  strcpy(Direction, directions[direction]);
}

The result is shown on the image below.

I tried playing around with the line of code for the mapping but did not have any success. Please advise, how can i rectify this problem?

I use a different method for handing the wind vane.

I keep 16 buckets, one for each direction supported by my wind vane:

int vaneBuckets[16];
float lastDir = 0.0; // degrees

On each loop (1000 times per second), I increment the bucket for the current vane direction:

	// Add a vane bucket every time through loop since it may swing around a lot
	vaneBuckets[adcToDirectionIndex(analogRead(VANE_PIN))]++;

The adcToDirectionIndex is implemented as follows. Your wind vane will likely have different constants:

// Dir    Res      Voltage  Value  Compass
// 0       33K     2.53V    3140   N
// 22.5     6.57K  1.31V    1626   NNE
// 45       8.2K   1.49V    1849   NE
// 67.5   891      0.27V     335   ENE
// 90       1K     0.30V     372   E
// 112.5  688      0.21V     261   ESE
// 135      2.2K   0.59V     732   SE
// 157.5    1.41K  0.41V     509   SSE
// 180      3.9K   0.92V    1142   S
// 202.5    3.14K  0.79V     980   SSW
// 225     16K     2.03V    2519   SW
// 247.5   14.12K  1.93V    2394   WSW
// 270    120K     3.05V    3785   W
// 292.5   42.12K  2.67V    3313   WNW
// 315     64.9K   2.86V    3549   NW
// 337.5   21.88K  2.26V    2805   NNW

float voltages[] = { 2.53, 1.31, 1.49, 0.27, 0.30, 0.21, 0.59, 0.41, 0.92, 0.79, 2.03, 1.93, 3.05, 2.67, 2.86, 2.26 };

int adcToDirectionIndex(int value) {
	float minDelta = 5.0;
	int direction = 0;

	float voltage = value * 3.3 / 4095;

	for(size_t ii = 0; ii < 16; ii++) {
		float delta = voltage - voltages[ii];
		if (delta < 0) {
			delta = -delta;
		}
		if (delta < minDelta) {
			minDelta = delta;
			direction = ii;
		}
	}
	return direction;
}

At startup and every 6 seconds I clear the buckets:

void clearVaneBuckets() {
	for(size_t ii = 0; ii < sizeof(vaneBuckets) / sizeof(vaneBuckets[0]); ii++) {
		vaneBuckets[ii] = 0;
	}
}

And finally every 6 seconds I calculate the current wind direction by taking the largest bucket (mode) as the current direction.

// Returns the most common direction in the last 6 seconds (or since clearVaneBucket was called)
// That mode in the mean/median/mode sense, not mode of operation.
float getVaneDirectionMode() {
	int max = 0;
	float dir = 0;

	for(size_t ii = 0; ii < sizeof(vaneBuckets) / sizeof(vaneBuckets[0]); ii++) {
		if (vaneBuckets[ii] > max) {
			dir = 22.5 * (float)ii;
			max = vaneBuckets[ii];
		}
	}
	return dir;
}
3 Likes

In that case you’d just need to adjust category mapping
e.g. like this

 direction = map(RawValue, 0, 4096, 1, 9);

In this case the last (9th) category will never be applied but the 8th should start correctly.

This might need some fine tuning, but since that code is so simple it shouldn’t take too many iterations.

@rickkas7
Thank you for responding so quickly. It took me a little while, but i finally managed to understand more or less the lines of you code you wrote :sweat_smile:. Thank you for sharing this with me, i have not come across this type of approach to programming for wind direction before (being relatively new to programming embedded systems) and would not have considered obtaining an average wind direction in this manner. Although the initial purpose of my wind vane was to indicate the instantaneous wind direction only i was intending on changing that at a later stage by incorporating an array that stores the average pot voltage every 200ms and than evaluating the average voltage in one second to determine the wind direction. And the code you demonstrated is a lot more complex then this approach!:sweat_smile: I will keep it in mind when i decide to make the change, thanks again.
For now, i would like to implement the code ScruffR suggested since it is more condensed and more elegant that what i have written. I just need some help to figure out how to get it to work the way it is suppose to.

I tried this, among other iterations and this is what It prints this when 3.3V is reached:

I am not sure why or how it is printing this text because this text is not even implemented as part of the array and this text that was written in void setup only! How do i eliminate this? Thanks in advance.

Have you also increased the max value for map() to 4096?
The problem with map() is that it's using integer division and the point where the result falls into 9 (which should only happen with a raw reading >= 4096, which isn't possible) is maybe not that clear cut due to order of execution of the individual steps of the calculation.
Hence you can slightly increase the max value (but no more than 4095+max category)

The text is printed since it's the next string found in flash memory following the array.
It comes from here

Normaly there should not be 9 as result, but you could just add a 10 element to the array with and empty string ("").

BTW, this is the implementation of map()

int map(int value, int fromStart, int fromEnd, int toStart, int toEnd)
    return (value - fromStart) * (toEnd - toStart) / (fromEnd - fromStart) + toStart;
}

When you plug in 4096 (or a slightly higher) for value and the other constants and apply the rules for integer divisions plus the order of execution you should be able to find why you got a false positive and how to avoid that.

I did not even think to do this since it exceeds the maximum value possible. Changing the mapping value 4095 to 4096 solves the problem. I am actually feeling a little silly and disappointed that i didn’t realize this on my own. Thank you very much for your help.

int map(int value, int fromStart, int fromEnd, int toStart, int toEnd)
return (value - fromStart) * (toEnd - toStart) / (fromEnd - fromStart) + toStart;
}

I even tried changing the variable "direction" to a float and the program would not compile now i understand why.

This part, however, is not clear to me. I am not sure what you mean by:

plus the order of execution you should be able to find why you got a false positive and how to avoid that.

what do you mean by false positive?

Whenever integer divisions happen you are almost always losing precision since any fraction in the result would just be dropped immediately.
While in normal mathematics the following calculations would render the same result, in computing they don’t due to integer maths and the order of execution.

x = (10 / 3) * 3; // x = 9
x = (3 * 10) / 3; // x = 10

So in regards to map() - before I looked up the implementation - I only knew that integer divisions will be used but not “where” in the process. The earlier in the calculation a division happens the greater the “uncertainty” in the result.
And due to that uncertainty it might be that the assertion that with a max value of 4096 the result could never be 9 may be proven wrong and hence render a “false positive” (a hit on a category that isn’t really one).

1 Like

@ScruffR
Ah i understand. Thank you very much for all your assistance.

@rickkas7 @armor
Thank you everyone.

1 Like