difficulty with interrupt routine

Status
Not open for further replies.

Fenichel

Well-known member
I am re-developing an environment-monitoring system that I first implemented with Arduinos. The system comprises a half-dozen or so peripheral stations, a base station, and a Windows application.
Each peripheral station is built around a Teensy 3.5. Various peripheral stations have various connected sensors around my house (current transformers; sensors of pressure, light, temperature, water depth, etc.; a rain gauge; and an anemometer/wind vane). The peripheral stations communicate with the base station using XBee transceivers. All the peripheral stations run the same code, and DIP switches on the peripheral-station board tell the station what sensors & display units it has.
The base station (also built around a Teensy 3.5) runs continuously, 24-7. It is connected to a PC running Windows through its USB port, but the Windows application runs only intermittently. The base station timestamps data reports from the peripheral stations, and then either (a) stores the data on its SD card, or, if the Windows application is running, (b) dumps stored data and newly-received data to the Windows application.
The problem I'm having is with the anemometer. The anemometer is conventionally constructed, with a rotor that spins as driven by the wind. The anemometer is connected to its peripheral station by two wires to form a normally-open circuit that is closed briefly once per revolution. A closure rate of 1 Hz corresponds to a wind speed of 1.492 MPH.
At the peripheral station, one of the two wires from the anemometer is grounded, and the other is (a) pulled up to 3V3 through a 10K resistor, and (b) connected to digital pin 24. An ISR attached to that pin, triggered by a falling level, counts revolutions, and conventional code does the rest.
The problem I'm having is that the system worked as designed for half a day or so, but then the anemometer events stopped being recorded. The hardware & software were not deliberately changed in any way, so I have some fear that an electrical problem has fried the Teensy's D24. Again, substantially identical code was used with Arduinos with no problems; that is why I think I may have inadvertently exceeded the dynamic tolerance of the Teensy.
The signal coming in to the pin looks like this
run.png.
The oscilloscope measures its range as [-720 mV, +4.16V], but my multimeter calls it [0, 3.1V]. At higher time-resolution, a serious switch-bounce is visible
bounce.png.
(I can see this with or without the Teensy attached) Double interrupts may have occurred, but the code (before interrupts stopped arriving on D24) avoided double counting.
The fall time of the signal is too short for accurate measurement with my equipment.
100ns.png
The code for the whole system is about 350 KB; I will upload all of it if anyone is willing to go through it. As a focused start, here is the top-level code of a peripheral station
Code:
// peripheral
// routines here have IDs 10xxx
#include "H:\application-specific\generic C compiler\DateFile.c"
// defines WhenCompiled

#include "G:\source code\Teensy\Teensy 3.5 pins.h"
// ************ pin assignments ************************
#define WindowsSerial Serial
#define XBeeSerial Serial1
// XBee uses                      pin00Serial1RX_MOSI1
// XBee uses                      pin01Serial1TX_MISO1
const int pinDHT22Data          = pin02Generic;
// BMP085 uses                    pin03SCL2
// BMP085 uses                    pin04SDA2;
const int pinTVDC               = pin05TV;
const int pinClearScreenInt     = pin06Generic;
#define OLEDSerial Serial3
// OLED uses                      pin07RX3
// OLED uses                      pin08TX3
const int pinRainGaugeInt       = pin09RX2CS0;
const int pinTVCS               = pin10TX2CS0TV;
const int pinTVData             = pin11MOSI0TV;
const int pinTVReset            = pin12MISO0;
const int pinTVSCLK             = pin13SCK0TV;
const int pinBlink              = pin13SCK0TV;
const int pinGND_HasTeensyView  = pin14A00;
const int pinGND_HasOLED        = pin15A01CS0TV;
const int pinGND_HasPhotocell   = pin16A02;
const int pinGND_HasDHT22       = pin17A03;
const int pinGND_HasCTs         = pin18A04;
const int pinGND_HasDepth       = pin19A05;
const int pinGND_HasBMP085      = pin20A06CS0;
const int pinGND_HasACDetect    = pin21A07CS0;
const int pinGND_HasRainGauge   = pin22A08;
const int pinGND_HasWindGauge   = pin23A09;
const int pinAnemometerInt      = pin24Generic;
const int pinDepthA             = pin25Generic;
const int pinDepthB             = pin26Generic;
const int pinACPowerLeft        = pin31A12RX4CS1;
const int pinACPowerRight       = pin32A13TX4SCK1;
const int pinDepth              = pin33A14TX5;
const int pinPhotocell          = pin34A15RX5;
const int pinGND_Option1        = pin35A16;
const int pinGND_Option2        = pin36A17;
const int pinGND_ShowBigTemp    = pin37A18SCL1;
const int pinGND_LargeOLED      = pin38A19SDA1;
const int pinWindDirection      = pin39A20;
#define   pinGND_InIDE                 A21
#define   pinGND_Speedup               A22
#define   pinAC_G                      A25
#define   pinAC_F                      A26

// unused
//   D27-D30        LL
//   A10, A11       inside UR

// ************ end pin assignments ************************
bool InIDE;
// routines 110xx
#include "G:\source code\Teensy\shared\Display.inc"

// routines 115xx
#include "G:\source code\Teensy\shared\TeensyView.inc"
tTeensyView TV;

// routines 112xx
#include "G:\source code\Teensy\shared\OLED.inc"
tOLED OLED;

// routines 118xx
#include "G:\source code\Teensy\Home Control 2\Peripheral3\include files\ClearScreen.inc"

// routines 12xxx
#include "G:\source code\Teensy\Home Control 2\Peripheral3\include files\infrastructure.inc"

// routines 13xxx
#include "G:\source code\Teensy\Home Control 2\Peripheral3\include files\line buffer.inc"

// routines 14xxx
#include "G:\source code\Teensy\Home Control 2\Peripheral3\include files\XBee.inc"
tXBee XBee;

// routines 15xxx
#include "G:\source code\Teensy\Home Control 2\shared\Reporter.inc"
tReporter Reporter;

// routines 16xxx
#include "G:\source code\Teensy\Home Control 2\Peripheral3\include files\ACDetector.inc"
tACDetector ACDetector;

// routines 17xxx
#include <i2c_t3.h>
#include "G:\source code\Teensy\Home Control 2\Peripheral3\include files\BMP085.inc"
Adafruit_BMP085_Unified BMP085;

// routines 18xxx
#include "G:\source code\Teensy\Home Control 2\Peripheral3\include files\CurrentTransformers.inc"
tCurrentTransformers CurrentTransformers;

// routines 19xxx
#include "G:\source code\Teensy\Home Control 2\Peripheral3\include files\Depth.inc"
tDepth Depth;

// routines 20xxx
const bool InThermostat = false;
#include "DHT.h"
DHT TheDHT(pinDHT22Data, DHT22);  // must precede DHTRRF inclusion
#include "G:\source code\Teensy\Home Control 2\shared\DHTRRF.inc"
tDHTRRF DHTRRF;

// routines 21xxx
#include "G:\source code\Teensy\Home Control 2\Peripheral3\include files\Photocell.inc"
tPhotocell Photocell;

// routines 22xxx
#include "G:\source code\Teensy\Home Control 2\Peripheral3\include files\rain gauge.inc"

// routines 23xxx
#include "G:\source code\Teensy\Home Control 2\Peripheral3\include files\anemometer.inc"
tWindGauge WindGauge;


void setup()
  { // routine ID 10000
    pinMode(pinBlink, OUTPUT);
    Serial.begin(9600);
    InIDE                  = IsGrounded(pinGND_InIDE,         "in IDE"    );
    if (InIDE)
      { while (not Serial); }

    HasACDetect            = IsGrounded(pinGND_HasACDetect,   "ACDetect"    );
    HasBMP085              = IsGrounded(pinGND_HasBMP085,     "BMP085"      );
    HasCurrentTransformers = IsGrounded(pinGND_HasCTs,        "HasCTs"      );
    HasDepth               = IsGrounded(pinGND_HasDepth,      "depth"       );
    HasDHT22               = IsGrounded(pinGND_HasDHT22,      "DHT22"       );
    HasOLED                = IsGrounded(pinGND_HasOLED,       "OLED"        );
    HasPhotocell           = IsGrounded(pinGND_HasPhotocell , "photocell"   );
    HasRainGauge           = IsGrounded(pinGND_HasRainGauge,  "rain"        );
    HasTeensyView          = IsGrounded(pinGND_HasTeensyView, "TeensyView"  );
    HasWindGauge           = IsGrounded(pinGND_HasWindGauge,  "wind"        );
    LargeOLED              = IsGrounded(pinGND_LargeOLED,     "large OLED"  );
    ShowBigTemp            = IsGrounded(pinGND_ShowBigTemp,   "show big TRH");

    if (not InIDE)
      { Serial.print("not "); }
    Serial.println("in IDE");
   
    //    Blink(2);

    if (HasTeensyView)                                       // TeensyView
      { char Message1[] = "About to start TeensyView";       // ..
        TellWindows(Message1, 10001);                        // ..
        TV.setup();                                          // ..
        Serial.println("back from TV");
      }                                                      // ..

    if (HasOLED)                                             // OLED
      { char Message2[] = "About to start OLED";             // ..
        TellWindows(Message2, 10002);                        // ..
        if (LargeOLED)                                       // ..
          { OLED.setupLarge(); }                             // ..
        else                                                 // ..
          { OLED.setup(); }                                  // ..
      }                                                      // ..
    SetupClearScreen();

    if (InIDE)                                               // XBee
      { char Message3[] = "XBee not used";                   // ..
        TellWindows(Message3, 10003); }                      // ..
    else                                                     // ..
      { char Message4[] = "About to start XBee";             // ..
        TellWindows(Message4, 10004);                        // ..
        if (not XBee.Begin())                                // ..
           { Hang("XBee inoperative"); }                     // ..
      }                                                      // ..

    if (HasACDetect)                                         // AC detector
      { char Message5[] = "Starting AC detector";            // ..
        TellWindows(Message5, 10005);                        // ..      
        ACDetector.setup();                                  // ..
      }                                                      // ..

    Reporter.setup();                                        // reporter

    if (HasBMP085)                                           // BMP085
      { char Message6[] = "Starting BMP085";                 // ..
        TellWindows(Message6, 10006);                        // ..
        BMP085.setup();                                      // ..
      }                                                      // ..

    if (HasCurrentTransformers)                              // current transformers
      { char Message7[] = "Starting current transformers";   // ..
        TellWindows(Message7, 10007);                        // ..
        CurrentTransformers.setup();                         // ..
      }                                                      // ..

    if (HasDepth)                                            // depth gauge
      { char Message8[] = "Starting depth gauge";            // ..
        TellWindows(Message8, 10008);                        // ..
        Depth.setup();                                       // ..
      }                                                      // ..

    if (HasDHT22)                                            // DHT22
      { char Message9[] = "Starting DHT22";                  // ..
        TellWindows(Message9, 10009);                        // ..
        DHTRRF.setup();                                      // ..
      }                                                      // ..

    if (HasPhotocell)                                        // photocell
      { char Message10[] = "Starting photocell";             // ..
        TellWindows(Message10, 10010);                       // ..
        Photocell.setup();                                   // ..
      }                                                      // ..

   if (HasRainGauge)                                         // rain gauge
      { char Message11[] = "Starting rain gauge";            // ..
        TellWindows(Message11, 10011);                       // ..
        SetUpRainGauge();                                    // ..
      }                                                      // ..

    if (HasWindGauge)                                        // anemometer
      { char Message12[] = "Starting wind gauge";            // ..
        TellWindows(Message12, 10012);                       // ..
        WindGauge.setup();                                   // ..
      }                                                      // ..

    char Message13[] = "setup complete";                     // done
    TellWindows(Message13, 10099);                           // ..
  } // setup
  
elapsedMillis msSinceLastDot;
unsigned int DotInterval = 30 * msOneSecond;

void loop()
  { // routine ID 10100
    Speedup = IsGrounded(pinGND_Speedup);
 
    if (msSinceLastDot > DotInterval)
      { if (TV.Available)
          { TV.PrintConstLine("."); }
         msSinceLastDot = 0;
      } // temporize

    ClearScreenCheck();

    if (HasACDetect)
      { ACDetector.loop(); }

    if (HasBMP085)
      { BMP085.loop(); }

    if (HasCurrentTransformers)
      { CurrentTransformers.loop(); }

    if (HasDepth)
      { Depth.loop(); }

    if (HasDHT22)
      { DHTRRF.loop(); }

    if (HasPhotocell)
      { Photocell.loop(); }

    if (HasRainGauge)
      { RainLoop(); }

    if (HasWindGauge)
      { WindGauge.loop(); }

  } // loop
and here is the code that supports the anemometer & wind vane.
Code:
// anemometer.inc
/* routine IDs 23xxx

    The anemometer contacts are closed for about 2/3 of each turn.  After the
 fall, there is one bounce to nearly full voltage before steady state is
 reached at about 60 us.  Before a rise, there is a fall to negative (!);
 steady state is reached in about 40 us.

    The wind-vane code assumes that the voltage measurement across the vane
 is made with a pull-up resistance of 10K.

   Initialize
     vector := (0,0)
   Every loop,
     Every msVaneInterval
       read wind vane
       convert direction to complex point
       add that point into vector
     Every msWindReportInterval
       get velocity
       get direction from vector
       report
       reset vector

*/
#define pi M_PI
 volatile int volAnemometerTurnCount;
   const bool DebugAnemometer = true;
unsigned long usEarliestPossibleWindTurn;

void AnemometerInterrupt()
  { // routine ID 23010
    const unsigned long usWindBounce = 100;
    // count revolution if this is not just a bounce
      unsigned long usNow;
      usNow = micros();
      if (usNow > usEarliestPossibleWindTurn)
        { volAnemometerTurnCount++;
          usEarliestPossibleWindTurn = usNow + usWindBounce;
        } // not a bounce
  } // AnemometerInterrupt

class tWindGauge
  { private:
                int msAdjustedInterval;
                int msReportInterval;
                int msVaneInterval;
                float SumRawDirection;
                float NInSum;
      elapsedMillis msSinceLastReport;
      elapsedMillis msSinceLastVane;
              float VectorX; // cos(direction)
              float VectorY; // sin(direction)
              float Velocity;
      void EstimateVelocity();
      void ReadWindVane();
      void ReportToBase();
    public:
      void loop();
      void setup();
  }; // tWindGauge

void tWindGauge::EstimateVelocity()
  { // routine ID
    const float mphOneHz = 1.492; // 1 Hz = 1.492 MPH
    float Hertz;
    char  Message[100];
    float NVTurnCount;
    float SecondsTurning;

    SecondsTurning = (float)msSinceLastReport / 1000.0;
    noInterrupts();                             // collect volatile results
      NVTurnCount = volAnemometerTurnCount;     // ..
      volAnemometerTurnCount = 0;               // ..
    interrupts();                               // ..
    Hertz = NVTurnCount / SecondsTurning;
    Velocity = mphOneHz * Hertz;
    if (DebugAnemometer)
      { sprintf(Message, "%.0f turns in %.2fs, %.1f MPH\n",
                  NVTurnCount,
                  SecondsTurning,
                  Velocity);
        TV.PrintConstLineln(Message);
      }
  } // tWindGauge::EstimateVelocity()

void tWindGauge::loop()
  { // routine ID

    if ((int)msSinceLastVane >= msVaneInterval)
      { ReadWindVane(); }

    if ((int)msSinceLastReport >= msReportInterval)
      { // time to report
        EstimateVelocity();
        if (InIDE)
          { Serial.printf("average raw direction (%.0f) = %.0f\n",
                          NInSum, SumRawDirection / NInSum); }
        ReportToBase();
        SumRawDirection = 0;
        NInSum = 0;
        VectorX = 0;
        VectorY = 0;
        msAdjustedInterval = msAdjustInterval(msReportInterval);
        msSinceLastReport = 0;
      } // time to report
  } // tWindGauge::loop()

void tWindGauge::ReadWindVane()
  { // routine ID
    float CompassDegrees;    // North 0, CW
    float EastDegrees;       // East 0, CW
    float DirectionRadians;  // East 0, CCW

    int RawVane = analogRead(pinWindDirection);
    SumRawDirection = SumRawDirection + RawVane;
    NInSum++;

    if      (RawVane <  75) { CompassDegrees = 112.5; } // ESE
    else if (RawVane <  88) { CompassDegrees =  67.5; } // ENE
    else if (RawVane < 110) { CompassDegrees =  90.0; } // E
    else if (RawVane < 155) { CompassDegrees = 157.5; } // SSE
    else if (RawVane < 214) { CompassDegrees = 135.0; } // SE
    else if (RawVane < 266) { CompassDegrees = 202.5; } // SSW
    else if (RawVane < 346) { CompassDegrees = 180.0; } // S
    else if (RawVane < 433) { CompassDegrees =  22.5; } // NNE
    else if (RawVane < 530) { CompassDegrees =  45.0; } // NE
    else if (RawVane < 614) { CompassDegrees = 247.5; } // WSW
    else if (RawVane < 666) { CompassDegrees = 225.0; } // SW
    else if (RawVane < 744) { CompassDegrees = 337.5; } // NNW
    else if (RawVane < 806) { CompassDegrees =   0.0; } // N
    else if (RawVane < 857) { CompassDegrees = 292.5; } // WNW
    else if (RawVane < 915) { CompassDegrees = 315.0; } // NW
    else                    { CompassDegrees = 270.0; } // W

    EastDegrees = CompassDegrees + 270.0;
    if (EastDegrees >= 360)
      { EastDegrees = EastDegrees - 360.0; }

    DirectionRadians = (360.0 - EastDegrees) * (pi /180.0);

    // convert direction to complex point, and add that point into vector
       VectorX = VectorX + cos(DirectionRadians);
       VectorY = VectorY + sin(DirectionRadians);
    msSinceLastVane = 0;
  } // tWindGauge::ReadWindVane()

void tWindGauge::ReportToBase()
  { // routine ID
    float HeadingEastZeroCCWRad;
    float HeadingNorthZeroCWRad;
    int   HeadingNorthZeroCWDeg;

    HeadingEastZeroCCWRad = atan2(VectorY, VectorX);
    HeadingNorthZeroCWRad = -HeadingEastZeroCCWRad + pi / 2.0;
    if (HeadingNorthZeroCWRad < 0)
      { HeadingNorthZeroCWRad = 2.0 * pi + HeadingNorthZeroCWRad; }
    if (InIDE)
      { Serial.printf("HEZCCWRad = %.2f (%.1f°), HNZCWRad = %.2f(%.1f°)\n",
                      HeadingEastZeroCCWRad, 180 * HeadingEastZeroCCWRad / pi,
                      HeadingNorthZeroCWRad, 180 * HeadingNorthZeroCWRad / pi);
      }
    HeadingNorthZeroCWDeg = (int)(180.0 * HeadingNorthZeroCWRad / pi) % 360;
    Reporter.SensorMessage_2('W', (int)(Velocity+0.5), HeadingNorthZeroCWDeg);
  } // tWindGauge::ReportToBase()
  
void tWindGauge::setup()
  { // routine ID
    volAnemometerTurnCount = 0;
    usEarliestPossibleWindTurn = 0;

    if (InIDE)
      { Serial.println("Setting up anemometer");
        msReportInterval = 20 * msOneSecond;
      }
    else
      { msReportInterval = 10 * msOneMinute; }
    msAdjustedInterval = msAdjustInterval(msReportInterval);
    msSinceLastReport =  msAdjustedInterval / 2;
    msSinceLastVane = 0;
    msVaneInterval = 250;
    VectorX = 0;
    VectorY = 0;
    SumRawDirection = 0;
    NInSum = 0;
    pinMode(pinAnemometerInt, INPUT_PULLUP);    //  each turn will short to ground
    attachInterrupt(pinAnemometerInt, AnemometerInterrupt, FALLING);
  } // tWindGauge::setup()
 
Is it possible that the under the hood changes around 8bit to 32bit and higher clock rates mean that you can overflow usEarliestPossibleWindTurn?
if (usNow > usEarliestPossibleWindTurn)
 
Is it possible that the under the hood changes around 8bit to 32bit and higher clock rates mean that you can overflow usEarliestPossibleWindTurn?
if (usNow > usEarliestPossibleWindTurn)

Thanks for the idea. I can see how wraparound might make usEarliestPossibleWindTurn falsely small, so that I might double-count a wave by (erroneously) counting the switch-bounce, but to get all counts to be missed, usEarliestPossibleWindTurn would have to be too big, not too small. In any event, (a) since usEarliestPossibleWindTurn is an unsigned long, there should be lots of time before overflow, and (b) the problem is not cured by power-cycling the Teensy, which should (at least for a while) avoid the overflow problem.
 
I am not sure why it might make a difference, but I have cleaned up the anemometer.inc code to use an elapsedMicros variable instead of an unsigned long.
Code:
// anemometer.inc
/* routine IDs 23xxx

    The anemometer contacts are closed for about 2/3 of each turn.  After the
 fall, there is one bounce to nearly full voltage before steady state is
 reached at about 60 us.  Before a rise, there is a fall to negative (!);
 steady state is reached in about 40 us.

    The wind-vane code assumes that the voltage measurement across the vane
 is made with a pull-up resistance of 10K.

   Initialize
     vector := (0,0)
   Every loop,
     Every msVaneInterval
       read wind vane
       convert direction to complex point
       add that point into vector
     Every msWindReportInterval
       get velocity
       get direction from vector
       report
       reset vector

*/
#define pi M_PI
 volatile int volAnemometerTurnCount;
   const bool DebugAnemometer = true;
elapsedMicros usSinceLastWindTurn;

void AnemometerInterrupt()
  { // routine ID 23010
    const unsigned long usWindBounce = 100;
    // count revolution if this is not just a bounce
    if (usSinceLastWindTurn > usWindBounce)
      { // too late to be bounce
        volAnemometerTurnCount++;
        usSinceLastWindTurn = 0;
      } // too late to be bounce
  } // AnemometerInterrupt

class tWindGauge
  { private:
                int msAdjustedInterval;
                int msReportInterval;
      elapsedMillis msSinceLastReport;
      elapsedMillis msSinceLastVane;
                int msVaneInterval;
              float VectorX; // cos(direction)
              float VectorY; // sin(direction)
              float Velocity;
      void EstimateVelocity();
      void ReadWindVane();
      void ReportToBase();
    public:
      void loop();
      void setup();
  }; // tWindGauge

void tWindGauge::EstimateVelocity()
  { // routine ID 23020
    const float mphOneHz = 1.492; // 1 Hz = 1.492 MPH
    float Hertz;
    char  Message[100];
    float NVTurnCount;
    float SecondsTurning;

    SecondsTurning = (float)msSinceLastReport / 1000.0;
    noInterrupts();                             // collect volatile results
      NVTurnCount = volAnemometerTurnCount;     // ..
      volAnemometerTurnCount = 0;               // ..
    interrupts();                               // ..
    Hertz = NVTurnCount / SecondsTurning;
    Velocity = mphOneHz * Hertz;
    if (DebugAnemometer)
      { sprintf(Message, "%.0f turns in %.2fs, %.1f MPH\n",
                  NVTurnCount,
                  SecondsTurning,
                  Velocity);
        TV.PrintConstLineln(Message);
      }
  } // tWindGauge::EstimateVelocity()

void tWindGauge::loop()
  { // routine ID 23030

    if ((int)msSinceLastVane >= msVaneInterval)
      { ReadWindVane(); }

    if ((int)msSinceLastReport >= msReportInterval)
      { // time to report
        EstimateVelocity();
        ReportToBase();
        VectorX = 0;
        VectorY = 0;
        msAdjustedInterval = msAdjustInterval(msReportInterval);
        msSinceLastReport = 0;
      } // time to report
  } // tWindGauge::loop()

void tWindGauge::ReadWindVane()
  { // routine ID 23040
    float CompassDegrees;    // North 0, CW
    float EastDegrees;       // East 0, CW
    float DirectionRadians;  // East 0, CCW

    int RawVane = analogRead(pinWindDirection);

    if      (RawVane <  75) { CompassDegrees = 112.5; } // ESE
    else if (RawVane <  88) { CompassDegrees =  67.5; } // ENE
    else if (RawVane < 110) { CompassDegrees =  90.0; } // E
    else if (RawVane < 155) { CompassDegrees = 157.5; } // SSE
    else if (RawVane < 214) { CompassDegrees = 135.0; } // SE
    else if (RawVane < 266) { CompassDegrees = 202.5; } // SSW
    else if (RawVane < 346) { CompassDegrees = 180.0; } // S
    else if (RawVane < 433) { CompassDegrees =  22.5; } // NNE
    else if (RawVane < 530) { CompassDegrees =  45.0; } // NE
    else if (RawVane < 614) { CompassDegrees = 247.5; } // WSW
    else if (RawVane < 666) { CompassDegrees = 225.0; } // SW
    else if (RawVane < 744) { CompassDegrees = 337.5; } // NNW
    else if (RawVane < 806) { CompassDegrees =   0.0; } // N
    else if (RawVane < 857) { CompassDegrees = 292.5; } // WNW
    else if (RawVane < 915) { CompassDegrees = 315.0; } // NW
    else                    { CompassDegrees = 270.0; } // W

    EastDegrees = CompassDegrees + 270.0;
    if (EastDegrees >= 360)
      { EastDegrees = EastDegrees - 360.0; }

    DirectionRadians = (360.0 - EastDegrees) * (pi /180.0);

    // convert direction to complex point, and add that point into vector
       VectorX = VectorX + cos(DirectionRadians);
       VectorY = VectorY + sin(DirectionRadians);
    msSinceLastVane = 0;
  } // tWindGauge::ReadWindVane()

void tWindGauge::ReportToBase()
  { // routine ID 23050
    float HeadingEastZeroCCWRad;
    float HeadingNorthZeroCWRad;
    int   HeadingNorthZeroCWDeg;

    HeadingEastZeroCCWRad = atan2(VectorY, VectorX);
    HeadingNorthZeroCWRad = -HeadingEastZeroCCWRad + pi / 2.0;
    if (HeadingNorthZeroCWRad < 0)
      { HeadingNorthZeroCWRad = 2.0 * pi + HeadingNorthZeroCWRad; }
    if (InIDE)
      { Serial.printf("HEZCCWRad = %.2f (%.1f°), HNZCWRad = %.2f(%.1f°)\n",
                      HeadingEastZeroCCWRad, 180 * HeadingEastZeroCCWRad / pi,
                      HeadingNorthZeroCWRad, 180 * HeadingNorthZeroCWRad / pi);
      }
    HeadingNorthZeroCWDeg = (int)(180.0 * HeadingNorthZeroCWRad / pi) % 360;
    Reporter.SensorMessage_2('W', (int)(Velocity+0.5), HeadingNorthZeroCWDeg);
  } // tWindGauge::ReportToBase()
  
void tWindGauge::setup()
  { // routine ID 23060
    if (InIDE)                                                       // reporting interval
      { Serial.println("Setting up anemometer");                     // ..
        msReportInterval = 20 * msOneSecond;                         // ..
      }                                                              // ..
    else                                                             // ..
      { msReportInterval = 10 * msOneMinute; }                       // ..
    msAdjustedInterval = msAdjustInterval(msReportInterval);         // ..
    msSinceLastReport =  msAdjustedInterval / 2;                     // ..

    msSinceLastVane = 0;                                             // wind vane
    msVaneInterval = 250;                                            // ..
    VectorX = 0;                                                     // ..
    VectorY = 0;                                                     // ..

    usSinceLastWindTurn = 1000 * msOneSecond;                        // anemometer
    volAnemometerTurnCount = 0;                                      // ..
    pinMode(pinAnemometerInt, INPUT_PULLUP);                         // ..
    attachInterrupt(pinAnemometerInt, AnemometerInterrupt, FALLING); // ..
  } // tWindGauge::setup()
This seems to be working, but of course the old code worked for a few hours before failing. In any event, I don't seem to have burnt out the pin; time will tell if it's a sneakier hardware problem (gradually rising local temperature or the like).
 
Sorry, had missed the part about this being a hard fault that persisted after resets, which is why my suggestion about overflows. Not having any ideas on how a pulsed input could hard fault after X runtime this way other than if pullups were misbehaving in some way or you had some form of aggressive interference that was flooding the input with changes. It is possible to change interrupt priority if you have deadlock conditions between timing and inputs but do not believe that could be a problem here.
 
Status
Not open for further replies.
Back
Top