Arduino library to read out a Weather Sensor Assembly p/n 80422 by Argent Data Systems
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

wind.h 5.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. /**
  2. * provides functions to read out the sensors of a SEN 08942 "Wettermesseinheit":
  3. * - Wind Direction: analog sensor with 16 cardinal direction steps
  4. * - Wind Speed: Interrupt counter
  5. * - Rain: Interrupt counter
  6. *
  7. * Check your controller's spec which pins have ADC or interrupt capabilities!
  8. * On a senseBox MCU this should work well:
  9. * WeatherUnit weather(4, 6, 5);
  10. */
  11. #pragma once
  12. #include <math.h>
  13. class WeatherUnit {
  14. public:
  15. WeatherUnit (uint8_t pinDirection, uint8_t pinSpeed, uint8_t pinRain, uint16_t directionOffset);
  16. void begin ();
  17. float getWindDir (unsigned char numMeasures, unsigned int interval);
  18. float getWindDir ();
  19. float getWindSpeed ();
  20. float getRain ();
  21. // interrupt handlers
  22. void countRain ();
  23. void countSpeed ();
  24. private:
  25. const unsigned int pinDirection, pinSpeed, pinRain, directionOffset;
  26. const unsigned int directionVolt[16] = {320, 410, 450, 620, 900, 1190, 1400, 1980, 2250, 2930, 3080, 3430, 3840, 4040, 4620, 4780};
  27. const unsigned int directionDegree[16] = {113, 68, 90, 158, 135, 203, 180, 23, 45, 248, 225, 338, 0, 292, 315, 270 };
  28. unsigned unsigned long lastResetRain = 0, lastResetSpeed = 0;
  29. volatile unsigned long rainCounter = 0, speedCounter = 0;
  30. volatile bool lastRainReading = 0, currentRainReading = 0, lastSpeedReading = 0, currentSpeedReading = 0;
  31. bool debounceInput (unsigned int pin, bool last);
  32. };
  33. // keep a pointer to the last created instance, in order to attach global interrupt handlers
  34. WeatherUnit* WEATHER_INSTANCE = NULL;
  35. void WEATHER_speedInterruptHandler () {
  36. WEATHER_INSTANCE->countSpeed();
  37. }
  38. void WEATHER_rainInterruptHandler () {
  39. WEATHER_INSTANCE->countRain();
  40. }
  41. WeatherUnit::WeatherUnit (uint8_t pinDirection, uint8_t pinSpeed, uint8_t pinRain, uint16_t directionOffset = 0)
  42. : pinDirection(pinDirection), pinSpeed(pinSpeed), pinRain(pinRain), directionOffset(directionOffset) {
  43. WEATHER_INSTANCE = this;
  44. }
  45. void WeatherUnit::begin () {
  46. lastResetRain = millis();
  47. lastResetSpeed = millis();
  48. pinMode(pinDirection, INPUT);
  49. pinMode(pinSpeed, INPUT_PULLUP);
  50. pinMode(pinRain, INPUT_PULLUP);
  51. attachInterrupt(digitalPinToInterrupt(pinSpeed), WEATHER_speedInterruptHandler, RISING);
  52. attachInterrupt(digitalPinToInterrupt(pinRain), WEATHER_rainInterruptHandler, LOW);
  53. }
  54. float WeatherUnit::getWindDir (unsigned char numMeasures, unsigned int interval) {
  55. // angular average
  56. float sumX = 0, sumY = 0, val;
  57. for (unsigned char i = 0; i < numMeasures; i++) {
  58. val = radians(getWindDir());
  59. sumX += sin(val);
  60. sumY += cos(val);
  61. if (i != numMeasures - 1) delay(interval);
  62. }
  63. return 180 + atan(sumX / sumY) * 180 / 3.1415;
  64. }
  65. float WeatherUnit::getWindDir () {
  66. unsigned int mvolt = analogRead(pinDirection);
  67. mvolt = map(mvolt, 0, 1023, 0, 5000); // map to range 0-5000 to make use of voltage values as defined in data sheet
  68. // search for correct index
  69. unsigned char i;
  70. for (i = 0; i < 16; i++) {
  71. if (mvolt <= directionVolt[i]) break;
  72. }
  73. i--; // the correct reference is actually lower than the reference value found.
  74. return (float) ((directionDegree[i] + directionOffset) % 360);
  75. }
  76. float WeatherUnit::getWindSpeed () {
  77. unsigned long now = millis();
  78. double secondsPassed = (now - lastResetSpeed) / 1000; // @FIXME: might yield bullshit when millis() overflows!
  79. // one impulse per second equals 2.4 km/h -> 0.6666 m/s
  80. float metersPerSec = (2 * speedCounter) / (3 * max(secondsPassed, 1.0));
  81. speedCounter = 0;
  82. lastResetSpeed = now;
  83. return metersPerSec;
  84. }
  85. float WeatherUnit::getRain () {
  86. float val = rainCounter * 0.2794; // mm aka L/m²
  87. rainCounter = 0;
  88. return val;
  89. }
  90. bool WeatherUnit::debounceInput (unsigned int pin, bool last) {
  91. bool current = digitalRead(pin);
  92. if (last != current) {
  93. // adjust this value.
  94. // shorter -> risk of detecting single switch multiple times,
  95. // longer -> risk of missing switches.
  96. delayMicroseconds(1e3);
  97. current = digitalRead(pin);
  98. }
  99. return current;
  100. }
  101. void WeatherUnit::countRain () {
  102. currentRainReading = debounceInput(pinRain, lastRainReading);
  103. if (lastRainReading == LOW && currentRainReading == HIGH)
  104. rainCounter++;
  105. lastRainReading = currentRainReading;
  106. }
  107. void WeatherUnit::countSpeed () {
  108. currentSpeedReading = debounceInput(pinSpeed, lastSpeedReading);
  109. if (lastSpeedReading == LOW && currentSpeedReading == HIGH)
  110. speedCounter++;
  111. lastSpeedReading = currentSpeedReading;
  112. }
  113. float mpsToKmph (float val) { return val * 3.6; }
  114. float mpsToKnots (float val) { return val * 1.943843307; }
  115. float mpsToBeaufort (float val) {
  116. if (val < 0.5) return 0;
  117. else if (val < 1.5) return 1;
  118. else if (val < 3.3) return 2;
  119. else if (val < 5.5) return 3;
  120. else if (val < 7.9) return 4;
  121. else if (val < 10.7) return 5;
  122. else if (val < 13.8) return 6;
  123. else if (val < 17.1) return 7;
  124. else if (val < 20.7) return 8;
  125. else if (val < 24.4) return 9;
  126. else if (val < 28.4) return 10;
  127. else if (val < 32.6) return 11;
  128. else return 12;
  129. }
  130. const String CARDINAL_DIRS_EN[16] = {"N", "NNE", "NE", "ENE", "E", "ESE", "SE", "SSE", "S", "SSW", "SW", "WSW", "W", "WNW", "NW", "NNW"};
  131. const String CARDINAL_DIRS_DE[16] = {"N", "N-NO", "N-O", "O-NO", "O", "O-SO", "S-O", "S-SO", "S", "S-SW", "S-W", "W-SW", "W", "W-NW", "N-W", "N-NW"};
  132. String degreeToCardinal (float val, const String cardinalNames[] = CARDINAL_DIRS_EN) {
  133. int i = (int)((val + 11.25) / 22.5);
  134. return cardinalNames[i % 16];
  135. }