Jump to content

Banner.jpg.b89429c566825f6ab32bcafbada449c9.jpg

Gina

Beyond the Event Horizon
  • Posts

    45,326
  • Joined

  • Last visited

  • Days Won

    120

Posts posted by Gina

  1. 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.

  2. 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.

  3. 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
      }
    }

     

  4. 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.

  5. 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.

  6. 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.

  7. This is the run up.  The PulseCount is in Number.  Mainly showing 8 counts which equates to 12mph but peaking 9 which equates to 13.5mph so 13mph for the gust speed is right and 12mph mean speed is right.  Now for the run down...

    1905461958_Screenshotfrom2020-09-0519-58-16.thumb.png.663c968d6b92c4e3ea1af4ed7ef1846c.png

  8. 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.

  9. Fan and anemometer stopped and this :-

    2030256417_Screenshotfrom2020-09-0517-02-24.thumb.png.95c22a56a7a79d0826ad96734e44ca1e.png

    Followed by this :-
    470653128_Screenshotfrom2020-09-0517-04-23.thumb.png.3cac401bbaf4e37d5af7e9be39064385.png

    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!

  10. 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!

    661996661_Screenshotfrom2020-09-0516-53-06.thumb.png.6fcb7b847eab8ab562064b2e5898b374.png

  11. 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!!

    257202525_Screenshotfrom2020-09-0515-46-26.thumb.png.77b97d85e6352afcd4fa746e1b5359f5.png

    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.

    1800821222_Screenshotfrom2020-09-0515-56-38.thumb.png.3193173d2d9ccfa84a90354bbb207a02.png

  12. 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.

  13. 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.

  14. 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).

    920075118_Screenshotfrom2020-09-0513-12-04.png.1b087eca5deb8cfd3445b06c3cc76fd9.png

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue. By using this site, you agree to our Terms of Use.