Jump to content

NLCbanner2024.jpg.2478be509670e60c2d6efd04834b8b47.jpg

Gina

Beyond the Event Horizon
  • Posts

    45,326
  • Joined

  • Last visited

  • Days Won

    120

Posts posted by Gina

  1. I was going to add the data from the rain gauge into the wind sensor unit with the gauge mounted on the same mast but now I'm having second thoughts and thinking it would be better fed to the ROR control unit as a second source for closing the roof, in addition to the rainfall information for the weather station - a "belt and braces" approach.

    I'm also thinking of adding a sky temperature sensor into the weather data somewhere and guess the ROR control unit would make sense for that too.

  2. I think I may use different periods for the wind speed and direction.  Although easier to have the same it isn't necessary and the ideal periods work out differently.

    I reckon ring arrays will work for both speed and direction and seems to me a better approach to consensus averaging for the direction.

  3. Example of modulus %

    /* update one value in an array each time through a loop */
    
    int values[10];
    int i = 0;
    
    void setup() {}
    
    void loop()
    {
      values[i] = analogRead(0);
      i = (i + 1) % 10;   // modulo operator rolls over variable  
    }

     

  4. There are 3 periods or intervals.  A 3s period used to count anemometer revolutions and to read the instantaneous wind direction then a 60s period used as the reporting (message sending) period and finally a 10m period for the mean direction.

  5. I need to consider my various periods.

    Quote

    The gust speed and direction are defined by the maximum three second average wind speed occurring in any period…overall wind intensity is defined by the average speed and direction over the ten-minute period leading up to the reporting time. Mean wind over other averaging periods may also be calculated.” Met Office

    ATM I'm using a 4.5s counting period for the anemometer speed count which would be good enough as the gust period since I'm not producing weather reports for publication.  I think a one minute reporting period would suffice for wind speed.  With a 10m period being standard for mean wind speed I could use a 10 integer ring buffer.

    With an integral number of 4.5s periods not fitting into a minute I think the irregular sampling could be a problem plus I would like to abide by the standard for gusts so I'm having second thoughts about the 4.5s counting period.  I could use a 3s period and adjust the result by multiplying by 1.5.  Only the very low speeds would be affected and accurate low wind speeds are not relevant.  This would mean 20 counting periods to one reporting period and 10 reporting periods for the period over which the mean and gust wind speed would be calculated.

  6. I'm still having a problem displaying the dashboard.  I'm wondering if I'm trying to display too much information.  Or too much changing information.  Most of my sensors are sending messages every 5s and several in quick succession.  It really isn't necessary to have the data updated this often and generally the actual values don't change that quickly.  The only reason for the rapidly changing data is noise.  The only truly rapidly changing data is wind gusts and even that doesn't want displaying every few seconds.  I think it would be better to average the data and send MQTT messages far less often.

    Or am I barking up the wrong tree here?

  7. Yes, I thought of doing that myself but not sure how easy it would be.  I hadn't thought of subtracting the oldest value from the sum and adding the newest though.

    A ring buffer for the consensus averaging was something I hadn't thought of but it makes a lot of sense as the direction is a ring.

  8. I think the easiest way of making a running average and maximum is to push them down a queue.

    A10 = A9
    A9 = A8
    A8 = A7
    etc.

    This can be simplified using an array and decreasing index.  The average and maximum can also be derived from the array.

  9. The next stage in the wind sensors is mathematical.  Rather than just the instantaneous direction and the wind speed taken over a few seconds, I want Mean and Gust wind speed and an average direction rather the a direction that varies all over the place with turbulence.

    The wind speed is straightforward.  The highest rev count in an extended time is taken as the gust speed and the average in that period is the Mean.  With the period being 10m and wanting readings more often than this I think I shall use a running average and maximum.

    For the wind direction there is a procedure called Consensus Averaging that collects a number of values in "bins" and then an average of the bins with the highest number of counts is taken.  This uses an array of 20 integers where the direction counts are collected.

  10. Sent the Gray code as well and that's working so it's in the Gray code to direction in binary that's wrong.

    Edit - found my error!  Working now.  16 discrete directions in sequence.  Only thing is, it's going anti-clockwise instead of clockwise as seen from above.  I can fix that easily enough.  Now edited the sketch above.

    Direction fixed and all working fine (as far as it goes).  This is just the instantaneous value and wants smoothing which I'll add next.

    This screenshot of MQTT Explorer shows the vane direction as I turned it (turned it a bit too far in the middle).

    434011851_Screenshotfrom2020-08-2516-17-23.png.808f1085a44d3fa61baed3507c233d72.png

     

  11. Here is the full sketch for the wind sensors so far.  This will read the rotary encoder on the vane and count revolution of the anemometer and send the instantaneous wind direction and wind speed to the MQTT network.

    // MQTT for wind sensors
    
    /*********
      Rui Santos
      Complete project details at https://randomnerdtutorials.com  
    ********
    Modified and added to by Gina 2020-08-21 onward
    ********
    */
    
    const int Interval = 4500;  //  Set sampling interval in milliseconds
    // 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;
    
    // Set GPIO for Hall Sensor
    const int HallSensorPin = 4;
    int PulseCount = 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);
    long lastMsg = 0;
    char msg[50];
    int value = 0;
    
    // Checks if Hall sensor was triggered
    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);
    }
    
    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();
    
      // Feel free to add more if statements to control more GPIOs with MQTT
    
    }
    
    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
    //      client.subscribe("esp32/output");
    //      client.subscribe("esp32/roof");
        } else {
          Serial.print("failed, rc=");
          Serial.print(client.state());
          Serial.println(" try again in 5 seconds");
          // Wait 5 seconds before retrying
          delay(5000);
        }
      }
    }
    void loop() {
      if (!client.connected()) {
        reconnect();
      }
      client.loop();
    
      long now = millis();
      if(now - lastMsg > Interval) {
    //    Serial.println("PulseCount :- ");
        lastMsg = now;
        
        // Convert the value to a char array
        char countString[8];
        dtostrf(PulseCount, 1, 0, countString);
        Serial.print("mph: ");
        Serial.println(countString);
        client.publish("wind/speed/mph", countString);
    
        // Get Beaufort value
        // Convert the value to a char array
        char BeaufortString[8];
        dtostrf(Beaufort(PulseCount), 1, 0, BeaufortString);
        Serial.print("Force: ");
        Serial.println(BeaufortString);
        client.publish("wind/speed/force", BeaufortString);
        PulseCount = 0;
        
        // Read wind vane optical sensor values
        dir1 = analogRead(An1);
        dir2 = analogRead(An2);
        dir3 = analogRead(An3);
        dir4 = analogRead(An4);
    /*    Serial.print(dir1);
        Serial.print("  ");
        Serial.print(dir2);
        Serial.print("  ");
        Serial.print(dir3);
        Serial.print("  ");
        Serial.print(dir4);
        Serial.println("  ");*/
    
        // Convert Gray bits to integer
        int Gray = 0;
        if (dir1 > 2000) {Gray = 8;};
        if (dir2 > 2000) {Gray = Gray + 4;};
        if (dir3 > 2000) {Gray = Gray + 2;};
        if (dir4 > 2000) {Gray = Gray + 1;};
    
        //  Convert Gray to binary
        dirn = 15 - codeArray[Gray];
        // Convert the value to a char array
        char dirString[8];
        dtostrf(dirn, 1, 0, dirString);
        Serial.print("Direction: ");
        Serial.println(dirString);
        client.publish("wind/direction", dirString);
      
      }
    }

     

  12. Now added Gray to binary conversion.

    // 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 code logic levels
    bool dirBit1 = false;
    bool dirBit2 = false;
    bool dirBit3 = false;
    bool dirBit4 = false;
    // 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;
        // Convert to logic levels
        dirBit1 = (dir1 > 2000);
        dirBit2 = (dir2 > 2000);
        dirBit3 = (dir3 > 2000);
        dirBit4 = (dir4 > 2000);
    
        // Convert bits to integer
        int Gray = 0;
        if (dir1 > 2000) {Gray = 8;};
        if (dir2 > 2000) {Gray = Gray + 4;};
        if (dir3 > 2000) {Gray = Gray + 2;};
        if (dir4 > 2000) {Gray = Gray + 1;};
    
        //  Convert Gray to binary
        dirn = codeArray[16];

    The dirBitx set isn't needed with this shortcut.

  13. Thinking...

    Gray code to binary :-

    1. 0000  0    0
    2. 0001  1    1
    3. 0011  3    2
    4. 0010  2    3
    5. 0110  6    4
    6. 0111  7    5
    7. 0101  5    6
    8. 0100  4    7
    9. 1100 12   8
    10. 1101 13   9
    11. 1111 15  10
    12. 1110 14  11
    13. 1010 10  12
    14. 1011 11  13
    15. 1001   9  14
    16. 1000   8  15

    int codeArray[16] = {0,1,3,2,7,6,4,5,15,14,12,13,8,9,11,10};

  14. Converting the ADC values to logic is easy

    // Gray code logic levels
    bool dirBit1 = false;
    bool dirBit2 = false;
    bool dirBit3 = false;
    bool dirBit4 = false;

    And in the loop;

        // Convert to logic levels
        dirBit1 = (dir1 > 2000);
        dirBit2 = (dir2 > 2000);
        dirBit3 = (dir3 > 2000);
        dirBit4 = (dir4 > 2000);

    Next I need to convert the Gray code to binary.  I've found complicated looking code in a Google search but I think there should be something simpler.

  15. Test satisfactory 🙂

    Added this code to the wind sensor ESP32 sketch.

    const int An1 = 34;
    const int An2 = 35;
    const int An3 = 32;
    const int An4 = 33;
    int dir1 = 0;
    int dir2 = 0;
    int dir3 = 0;
    int dir4 = 0;

    And in void loop()

      dir1 = analogRead(An1);
      dir2 = analogRead(An2);
      dir3 = analogRead(An3);
      dir4 = analogRead(An4);
      Serial.print(dir1);
      Serial.print("  ");
      Serial.print(dir2);
      Serial.print("  ");
      Serial.print(dir3);
      Serial.print("  ");
      Serial.print(dir4);
      Serial.println("  ");

    The readings were either <200 or >3500 so the midrange value of 2000 will be fine as the threshold.  As expected the different ADC readings changed one at a time according to the Gray code.

     

  16. Testing shows 6mA diode current with 6v supply.  At that the the ON voltage across a 2K2 resistor is just below the Vcc and the OFF (interrupted light beam) is 0.5v.  So a threshold of 1.5v (half scale) should be fine.  To check all the optical sensors I think I'll write a sketch to read the value of each sensor and display the voltages (ADC readings).

     

×
×
  • 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.