-
Posts
45,326 -
Joined
-
Last visited
-
Days Won
120
Content Type
Profiles
Forums
Gallery
Events
Blogs
Posts posted by Gina
-
-
I will get rid of all constants changed from integers to unsigned long because if these overlap other variables I would expect trouble in the future as this is to do with millis mounting up past the normal integer limit as time goes on. Seems likely that this was why the wind speed calculations suddenly went wrong several days after the system was set up. Let's see... millis will exceed the integer limit at 32767 ms = 32.7s and the next byte at 256 times that giving 2.3 hours. it will encroach on the next byte in just under 25days. Just what damage would result depends on what data or program code follows.
-
This is the latest sketch including debugging code I'll run it tomorrow - not doing any more tonight.
// Wind speed and direction 2020-09-05 /********* 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 unsigned long P3m = 180000; // variable used as constant unsigned long last3s = 0; unsigned 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; // debugging the PulseCount int pulseArray[10] = {0,0,0,0,0,0,0,0,0,0}; int writeIndex = 0, readIndex = 0; // Speed variables long sumSpeed = 0; // 0- int meanSpeed = 0; // 0-100mph int gustSpeed = 0; // 0-100mph // Set GPIO for Hall Sensor const int HallSensorPin = 4; int PulseCount = 0, windGust = 0; int 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=1; windSpeed += PulseCount; // Accumulate mean speed if (PulseCount > windGust) {windGust = PulseCount;}; // Get max speed for gust pulseArray[writeIndex] = PulseCount; writeIndex = (writeIndex+1)%10; 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() { ++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 sendringIndex(){ // Convert the number to a char array char numString[8]; dtostrf(ringIndex, 1, 0, numString); Serial.print("ringIndex: "); Serial.println(numString); client.publish("wind/ringIndex", numString); } 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(){ sendringIndex(); sendNumberMessage2(windGust); // sendNumberMessage2(windSpeed); // 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 sumSpeed = 0; gustSpeed = 0; windSpeed = 0; windGust = 0; for (int i = 0; i < 10; i++) { sumSpeed += meanArray[i]; // sum the windSpeeds if (gustArray[i] > gustSpeed){gustSpeed = gustArray[i];}} // find maximum gust speed sumSpeed *=3; // in conjunction with 20 converts speed by 1.5 meanSpeed = sumSpeed /20 /timerCount; // gustSpeed = gustSpeed *3 /2; // Speed count over 3s rather than 4.5s sendSpeedMessages(); ringIndex = (ringIndex+1)%10; // move ringIndex on to next location in the ring arrays } // debugging PulseCount void sendPulseArray(){ sendNumberMessage(pulseArray[readIndex]); readIndex = (readIndex+1)%10; // move readIndex on to next location in the pulse ring array } 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(); if (interrupts != 0 ){ // ISR has triggered - proceed and perform the jobs if (timerCount >= 21) {speed21sum(); timerCount=0;}; // time the various periods long now = millis(); if(now - last3s > P3s) {getDirection(); sendPulseArray(); last3s = now;} if(now - last3m > P3m) {ConsensusAveraging(); last3m = now;} interrupts = 0; // Reset the flag } }
-
Yes, this is the PulseCount history.
-
In integer arithmetic the remainder is simply discarded, no rounding up is applied if the remainder is above half. I agree that this means division should be left until the end of a calculation to keep as much precision as possible provided no values exceed the range of the particular integer type. I'll go through my code and apply that principle where possible.
-
The rogue PulseCount only occurs when the anemometer is stationary. I have never seen anything wrong when the anemometer is rotating.
There are no float types in this sketch. All variables are either integer types, strings or char arrays. The latter are needed by MQTT. I could multiply by 3 and divide by 2 rather than multiply by 1.5 I guess but all the calculations now appear to be correct.
I guess I could just detect the rogue value and reject it but I would rather know the cause.
-
Well, I don't know. It's just a single event - one high count.
-
-
-
1 hour ago, globular said:
Vibration causing a one-off blip in the pulse that doubles the gust will not really show in the mean as you are averaging over a large number of pulses and only showing the average in round mph. One of your earlier tests had a larger gust blip and there was an increase in the mean then too.
I suggest you re-run the test but rather than turning off the fan (with associated vibrations) put a piece of card in the way to divert the wind - very carefully so as not to knock anything.
You'll then know if it's a physical vibration issue or some problem in the code when pulses stop.
I think you may have a good point so I've moved the fan off the table onto a different "perch" that won't transmit any vibration to the table. Plenty of space between fan and anemometer so no problem with a piece of cardboard to stop the breeze.
-
-
-
To see if it is a burst of pulses as the anemometer stops I think I'll save the PulseCount into a ringArray in the timer ISR then read out the ringArray outside the ISR.
-
This leads to this code where the windGust variable was last accessed.
void speed21sum(){ int divisor = 140; sendringIndex(); sendNumberMessage(windGust); sendNumberMessage2(windSpeed); // 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 }
windGust is reset to zero in this code. Q.E.D.!!!
I've been through the sketch with Find and there are no occurrences of any of the variables in question other than where I've looked.
I thought one possibility might be that the anemometer stopped right on the edge of the Hall sensor operation and vibration caused multiple pulses but if this happened the mean value would show a small jump, which it doesn't seem to do. To be sure I would have to check the PulseCount at each timer interrupt event.
-
Fan and anemometer stopped and this :-
Showing one rogue raw gust value (of 17 equating to a speed of 25mph) followed by the correct zeros. So where did that rogue raw count come from???
As usual the rogue gust value persisted in the gust ringArray until replaced by 10 zeros (ringIndex=1).
The rogue value only seems to exist in the windGust variable - there is no sign of any perturbation in the windSpeed, eliminating a rogue PulseCount value. This limits the part of the code under suspicion to the timer ISR code relating to the gust speed.
void IRAM_ATTR onTime() { portENTER_CRITICAL_ISR(&timerMux); interrupts=1; 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); }
This would appear to be the line of code that causes the rogue gust speed
if (PulseCount > windGust) {windGust = PulseCount;}; // Get max speed for gust
Or is it??? If windGust is high when this line of code is run it won't be changed!
-
Watching the ringIndex, windGust and windSpeed. Got the fan a bit closer and gust speed of 13mph. Number is the raw gust speed (counts in 3s) and Number2 is the speed summed over 21 3s intervals for the mean. Next to switch off the fan and see what happens. I may have to extend the debugging to show all the ringArray values - quit a lot of data!
-
What to do now - that's the question...
The mean speed seems to be working alright now. Mostly the gust speed is alright but sometimes getting these rogue values. Guess it's time to add some debugging.
-
Urck!! Just thought I'd test the rig with the top assembled. Doesn't look like it'll be deployed today!! Fan on and 10mpg gust speed. Mean rose to 9 as usual. Turned fan off and let the anemometer slow down and stop. While this is only a display and for information only I don't really want ridiculous readings!!
After 10m the giant gust had propagated round the ring Array and the mean speed gone down to zero. Next minute the gust speed read zero too.
-
-
-
Update... Gust gauges now show zero so it seems the 73mph was an instantaneous value which propagated through the gust ringArray until it was replaced by the correct zero value. I'll do some more testing...
Fan on low and anemometer rotating at about 1 rev per second. Gust showing 3mph.
-
Mean speed have now decreased to zero which is right but the gust continues to show 73mph!! Back to the drawing board then and no deployment!!
-
-
Mean speed has now increased to 10mph.
Got one more test before deploying the wind sensors - reduced the fan speed. Now watching for the speed readings to decrease over the next 10m or so. I don't expect the gust speed to change until more than 10m have elapsed but the mean speed should gradually decrease over the 10m period.
-
Since the Mean Speed is summed over a long period I could read it to greater precision than integer but I guess wind speed in whole miles per hour is quite sufficient and "if it ain't broke, don't fix it". The gust speed is only accurate to units of 1.5mph as the calibration factor is mph in 4.5s and I'm reading in 3s periods and correcting afterwards.
The test rig has been running correctly for a while now so I guess I can put everything back together and put the rig back on the observatory. Having found a definite cause of problems and fixed it I feel reasonably confident that it should be ready for deployment - again.
The anemometer rotation rate has just increased a bit - guess the mains voltage has increased and with it the fan speed. I'll just watch it a bit longer and see if the mean speed increases too as it should (slowly).
Weather Station Ideas
in DIY Astronomer
Posted · Edited by Gina
As to what to do about the spike sometimes occurring when the anemometer is stationary, whilst I would like to know why and cure the cause I'm wondering if my time would be better spent on just killing it and getting on with other things. I could just ignore it as the effects only last 10m.