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. Working fine I think, though won't know for sure until I get the wind sensors connected and the wind blowing on it.  Looks like the UL constant(s) has been causing the problems all along and that the Arduino Reference is wrong!

    648612023_Screenshotfrom2020-09-0509-58-12.png.874db2a15258abf3c902c4b1e5607d45.png

  2. I thought adding U or L or UL allocated the extra space but to avoid any possible trouble I've declared as follows

    // 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;

     

  3. The hardware timer works.

    I don't quite understand "While the Arduino manual is right you cannot declare an int and stuff it with an unsigned long."  I've thought about this, it seems to work fine but I guess I could assign an unsigned long variable and preset it with the value I want and use it as a constant.  After all, a non-varying variable is a constant in effect.  Would that be better?

    Yes, the compiler should have picked up that error.  Guess nothing is perfect and the error checking is pretty good.  Though sometimes the error message can be rather vague or unhelpful.

  4. 14 minutes ago, skybadger said:

    Don't write debug code  in interrupts that write to serial.!

    I'm not.

    16 minutes ago, skybadger said:

    You are using the hardware timer, there are only a few available .  On the 8266 you can chain many more using the soft interrupt timer , I don't know if this is true for esp32.

    I'm only running one hardware timer - the ESP32 has 4.  All other timings are by counting or using millis and now.

  5. 12 minutes ago, skybadger said:

    Also, dont sit and wait in the loop for things to fire. A loop happens  every 5ms or so, so loop should look for a timer  interrupt fired flag to indicate the desired event has happened rather than block. On do ding the flag you can then process the action. Otherwise,  while interrupts will keep on firing, your Wi-Fi may crap out due to missing carrier detects.

    OK took that from elsewhere - changed it to

    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(); last3s = now;}
        if(now - last3m > P3m) {ConsensusAveraging(); last3m = now;}
        interrupts = 0; // Reset the flag
      }
    }

     

  6. 11 minutes ago, skybadger said:

    Looking at the code I would note that constant int xxxx = 1234ul will have unintended outcomes.

    Got that from the Arduino Reference for Constants.

    Quote

    U & L formatters:

    By default, an integer constant is treated as an int with the attendant limitations in values. To specify an integer constant with another data type, follow it with:

    • a 'u' or 'U' to force the constant into an unsigned data format. Example: 33u

    • a 'l' or 'L' to force the constant into a long data format. Example: 100000L

    • a 'ul' or 'UL' to force the constant into an unsigned long constant. Example: 32767ul

     

  7. 42 minutes ago, JamesF said:

    The mention of a backtrace before the reboot suggests to me that something has been caught running out of bounds or something like that.  Perhaps run off the end of an array into memory you're not allowed to use?  Attempting to dereference a null pointer might be another favourite, but I don't recall you doing any pointery type stuff in your code.

    James

    The problem was trying to increment a float.  Now fixed.

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

     

  9. Think I may have it sussed.  Used the timer interrupt just to count the anemometer pulses plus a count of times it was enacted to use later for the divisor.

    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);
    }

    This sets the interrupts flag, counts the anemometer pulses, calculating sum and max for mean and gust speeds.  Then the PulseCount is cleared for the next count and the number of times this ISR is run is counted in timerCount.

  10. Decided to put the speed part of the 3s jobs directly in the ISR.

    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;
      portEXIT_CRITICAL_ISR(&timerMux);
    }

     

  11. I have certainly considered taking the sensor etc. out of the casing but the lens to sensor distance has to be accurate to microns!!  In the past I have used fixed focus lenses in my ASCs but using the mounting thread for focussing was far too coarse.  The lens I'm using now has a focus ring and focuses using a very fine thread.  This is a lot better but still needs just a few degrees rotation to take the focus from good to dreadful.

    I have a few ideas for finer focussing.  I can get rid of backlash with a spring but have to be careful to avoid plastic creep.  One idea is a lever and cam.  Another is a threaded rod and nut moving the lever.

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