I have renamed the routines better to reflect the operations and separated speed and direction processes. The Timer and Hardware ISRs are now next to each other with the constant and variable declarations first. I have changed the speed averaging period to 21x3s intervals to make the divisor an integer when dividing the sum and correcting the speed (speed integration time 3s rather than the 4.5s which would have given mph directly).
This is the sketch.
// Wind speed and direction 2020-09-04
/*********
With thanks to Rui Santos
Complete project details at https://randomnerdtutorials.com
and others
********
Modified and added to by Gina 2020-08-21 onward
********
*/
// Set up for timer interrupts
volatile int interrupts;
int totalInterrupts;
hw_timer_t * timer = NULL;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
int timerCount = 0;
// periods or intervals in milliseconds
const int P3s = 3000; // integer
const int P3m = 180000ul; // unsigned long
long last3s = 0;
long last3m = 0;
// ADC pins
const int An1 = 34;
const int An2 = 35;
const int An3 = 32;
const int An4 = 33;
// ADC readings
int dir1 = 0;
int dir2 = 0;
int dir3 = 0;
int dir4 = 0;
// Gray to binary table
int codeArray[16] = {0,1,3,2,7,6,4,5,15,14,12,13,8,9,11,10};
int dirn = 0;
// Direction count bins
int bin[20] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
// 10m speed averaging etc.
int meanArray[10] = {0,0,0,0,0,0,0,0,0,0};
int gustArray[10] = {0,0,0,0,0,0,0,0,0,0};
int ringIndex = 0;
// Output variables
int Direction = 0; // 0-359 degrees
int meanSpeed = 0; // 0-100mph
int gustSpeed = 0; // 0-100mph
// Set GPIO for Hall Sensor
const int HallSensorPin = 4;
int PulseCount = 0, windGust = 0;
float windSpeed = 0;
#include <WiFi.h>
#include <PubSubClient.h>
// Replace the next variables with your SSID/Password combination
const char* ssid = "Ubiquity";
const char* password = "********";
const char* mqtt_server = "192.168.1.140";
WiFiClient windClient;
PubSubClient client(windClient);
void IRAM_ATTR onTime() {
portENTER_CRITICAL_ISR(&timerMux);
interrupts++;
windSpeed += PulseCount; // Accumulate mean speed
if (PulseCount > windGust) {windGust = PulseCount;}; // Get max speed for gust
PulseCount = 0; // clear count
timerCount++; // count number of timer interrupts for 1m speed sum
portEXIT_CRITICAL_ISR(&timerMux);
}
// Checks if Hall sensor was triggered - Interrupt Handler
void IRAM_ATTR HallTriggered() {
// Serial.println("Hall Triggered");
++PulseCount; // Increment count
}
int Beaufort(int mph){
if (mph < 1) return 0;
else if (mph <= 3) return 1;
else if (mph <= 7) return 2;
else if (mph <= 12) return 3;
else if (mph <= 18) return 4;
else if (mph <= 24) return 5;
else if (mph <= 31) return 6;
else if (mph <= 38) return 7;
else if (mph <= 46) return 8;
else if (mph <= 54) return 9;
else if (mph <= 63) return 10;
else if (mph <= 72) return 11;
else return 12;
}
void setup() {
Serial.begin(115200);
setup_wifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
// Hall Sensor mode INPUT_PULLUP
pinMode(HallSensorPin, INPUT_PULLUP);
// Set HallSensor pin as interrupt, assign interrupt function and set FALLING mode
attachInterrupt(digitalPinToInterrupt(HallSensorPin), HallTriggered, FALLING);
if (!client.connected()) {reconnect();}
//
// Configure Prescaler to 80, as our timer runs @ 80Mhz
// Giving an output of 80,000,000 / 80 = 1,000,000 ticks / second
timer = timerBegin(0, 80, true);
timerAttachInterrupt(timer, &onTime, true);
// Fire Interrupt every 3m ticks, so 3s
timerAlarmWrite(timer, 3000000, true);
timerAlarmEnable(timer);
}
void setup_wifi() {
delay(10);
// We start by connecting to a WiFi network
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
void callback(char* topic, byte* message, unsigned int length) {
Serial.print("Message arrived on topic: ");
Serial.print(topic);
Serial.print(". Message: ");
String messageTemp;
for (int i = 0; i < length; i++) {
Serial.print((char)message[i]);
messageTemp += (char)message[i];
}
Serial.println();
}
void reconnect() {
// Loop until we're reconnected
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Attempt to connect
if (client.connect("windClient")) {
Serial.println("connected");
// Subscribe
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
//Get instantaneous wind direction - variable dirn
int readDirection(){
// Read wind vane optical sensor values
dir1 = analogRead(An1);
dir2 = analogRead(An2);
dir3 = analogRead(An3);
dir4 = analogRead(An4);
// Convert Gray bits to integer
int Gray = 0;
if (dir1 > 2000) {Gray = 8;};
if (dir2 > 2000) {Gray += 4;};
if (dir3 > 2000) {Gray += 2;};
if (dir4 > 2000) {Gray += 1;};
// Convert Gray to binary
dirn = 15 - codeArray[Gray]; // correct rotation direction
dirn = (dirn - 1) %16; // correct encoder for North
return dirn;
}
//
void sendSpeedMessages(){
if (!client.connected()) {reconnect();}
// messages to send :-
// wind/speed/mph -- meanSpeed
// wind speed/force -- Beaufort(meanSpeed)
// wind/gust/mph -- gustSpeed
// wind/gust/force -- Beaufort(gustSpeed)
//
// Convert the Mean Speed to a char array
char msString[8];
dtostrf(meanSpeed, 1, 1, msString);
Serial.print(" Mean Speed: ");
Serial.print(msString);
client.publish("wind/speed/mph", msString);
// Convert the Mean-Speed-Force to a char array
char bsString[8];
dtostrf(Beaufort(meanSpeed), 1, 0, bsString);
// Serial.print("Force: ");
// Serial.println(bsString);
client.publish("wind/speed/force", bsString);
// Convert the Gust-Speed to a char array
char gsString[8];
dtostrf(gustSpeed, 1, 0, gsString);
Serial.print(" Gust: ");
Serial.println(gsString);
client.publish("wind/gust/mph", gsString);
// Convert the Gust-Speed-Force to a char array
char bgString[8];
dtostrf(Beaufort(gustSpeed), 1, 0, bgString);
// Serial.print("Gust Force: ");
// Serial.println(bgString);
client.publish("wind/gust/force", bgString);
}
void sendDirectionMessage(int Dir){
// Convert the Direction to a char array
char dirString[8];
dtostrf(Dir, 1, 0, dirString);
// Serial.print(" Direction: ");
// Serial.println(dirString);
client.publish("wind/direction", dirString);
}
void sendDirectionMessageInst(int Dir){
// Convert the Direction to a char array
char dirString[8];
dtostrf(Dir, 1, 0, dirString);
// Serial.print("Transient Direction: ");
// Serial.println(dirString);
client.publish("wind/direction/inst", dirString);
}
/* Debugging only
void sendNumberMessage(int N){
// Convert the number to a char array
char numString[8];
dtostrf(N, 1, 0, numString);
Serial.print("Number: ");
Serial.println(numString);
client.publish("wind/number", numString);
}
void sendNumberMessage2(int N){
// Convert the number to a char array
char numString[8];
dtostrf(N, 1, 0, numString);
Serial.print("Number: ");
Serial.println(numString);
client.publish("wind/number2", numString);
}
*/
void getDirection(){
byte instDir = readDirection();
++bin[instDir]; // increment appropriate bin
sendDirectionMessageInst(instDir); // send instantaneous direction
}
void speed21sum(){
int divisor = 140;
Serial.print("ringIndex: ");
Serial.print(ringIndex);
// wind speed mean and gust ring arrays and report
meanArray[ringIndex] = windSpeed; // put windSpeed into new array index
gustArray[ringIndex] = windGust; // put windGust into new array index
meanSpeed = 0; gustSpeed = 0; windSpeed = 0; windGust = 0;
for (int i = 0; i < 10; i++) {
meanSpeed += meanArray[i]; // sum the windSpeeds
if (gustArray[i] > gustSpeed){gustSpeed = gustArray[i];}} // find maximum gust speed
divisor = timerCount * 10 / 1.5; // with timerCount=21 this gives a whole number
meanSpeed /=divisor; // the first sum was over 21 values and then the second over 10 values
gustSpeed *= 1.5; // Speed count over 3s rather than 4.5s
sendSpeedMessages();
ringIndex = (ringIndex+1)%10; // move ringIndex on to next location in the ring arrays
}
void ConsensusAveraging(){
// wind direction calculations and report
int sum[16];
int S=0,I=0,W=0;
bin[16] = bin[0];
bin[17] = bin[1];
bin[18] = bin[2];
bin[19] = bin[3];
for (int i = 0; i < 16; i++) {sum[i] = bin[i] + bin[i+1] + bin[i+2] + bin[i+3] + bin[i+4];
// find the index with the highest sum and save sum and index
if (sum[i] > S){S = sum[i]; I = i;};}
W = (bin[I+1] + 2 * bin[I+2] + 3 * bin[I+3] + 4 * bin[I+4]) * 45 / S;
sendDirectionMessage((I * 45 + W)%720 /2);
//Empty the bins ready for a new direction calculation
for (int i = 0; i < 16; i++) {bin[i] = 0;};
}
void loop() {
// if (!client.connected()) {reconnect();}
client.loop();
while( interrupts == 0 ){} // will spin here until the ISR sets the flag
// ISR has triggered - proceed and perform the jobs
if (timerCount >= 21) {speed21sum();};
// time the various periods
long now = millis();
if(now - last3s > P3s) {getDirection(); last3s = now;}
if(now - last3m > P3m) {ConsensusAveraging(); last3m = now;}
interrupts = 0; // Reset the flag
}