├── CONTRIBUTING.md
├── LICENSE.md
├── README.md
├── documents
└── Weather_Meter_Kit_Datasheet.pdf
├── examples
├── Example1_BasicReadings
│ └── Example1_BasicReadings.ino
├── Example2_ManualCalibration
│ └── Example2_ManualCalibration.ino
└── Example3_CalibrationHelper
│ └── Example3_CalibrationHelper.ino
├── keywords.txt
├── library.properties
└── src
├── SparkFun_Weather_Meter_Kit_Arduino_Library.cpp
├── SparkFun_Weather_Meter_Kit_Arduino_Library.h
└── SparkFun_Weather_Meter_Kit_Constants.h
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | Thank you so *much* for offering to help out. We truly appreciate it.
4 |
5 | If you'd like to contribute, start by searching through the [issues](https://github.com/sparkfun/SparkFun_Weather_Meter_Kit_Arduino_Library/issues) and [pull requests](https://github.com/sparkfun/SparkFun_Weather_Meter_Kit_Arduino_Library/pulls) to see whether someone else has raised a similar idea or question.
6 | Please check the [closed issues](https://github.com/sparkfun/SparkFun_Weather_Meter_Kit_Arduino_Library/issues?q=is%3Aissue+is%3Aclosed)
7 | and [closed pull requests](https://github.com/sparkfun/SparkFun_Weather_Meter_Kit_Arduino_Library/pulls?q=is%3Apr+is%3Aclosed) too - you may find that your issue or feature has already been discussed.
8 |
9 | If you decide to add a feature to this library, please create a PR and follow these best practices:
10 |
11 | * Change as little as possible. Do not submit a PR that changes 100 lines of whitespace. Break up into multiple PRs if necessary.
12 | * If you've added a new feature document it with a simple example sketch. This serves both as a test of your PR and as a quick way for users to quickly learn how to use your new feature.
13 | * If you add new functions also add them to _keywords.txt_ so that they are properly highlighted in Arduino. [Read more](https://www.arduino.cc/en/Hacking/libraryTutorial).
14 | * **Important:** Please submit your PR using the [release_candidate branch](https://github.com/sparkfun/SparkFun_Weather_Meter_Kit_Arduino_Library/tree/release_candidate). That way, we can merge and test your PR quickly without changing the _master_ branch
15 |
16 | 
17 |
18 | ## Style guide
19 |
20 | Please read and follow the [Arduino API style guide](https://www.arduino.cc/en/Reference/APIStyleGuide). Also read and consider the [Arduino style guide](https://www.arduino.cc/en/Reference/StyleGuide).
21 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | SparkFun License Information
2 | ============================
3 |
4 | SparkFun uses two different licenses for our files — one for hardware and one for code.
5 |
6 | Hardware
7 | ---------
8 |
9 | **SparkFun hardware is released under [Creative Commons Share-alike 4.0 International](http://creativecommons.org/licenses/by-sa/4.0/).**
10 |
11 | Note: This is a human-readable summary of (and not a substitute for) the [license](http://creativecommons.org/licenses/by-sa/4.0/legalcode).
12 |
13 | You are free to:
14 |
15 | Share — copy and redistribute the material in any medium or format
16 | Adapt — remix, transform, and build upon the material
17 | for any purpose, even commercially.
18 | The licensor cannot revoke these freedoms as long as you follow the license terms.
19 | Under the following terms:
20 |
21 | Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.
22 | ShareAlike — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original.
23 | No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.
24 | Notices:
25 |
26 | You do not have to comply with the license for elements of the material in the public domain or where your use is permitted by an applicable exception or limitation.
27 | No warranties are given. The license may not give you all of the permissions necessary for your intended use. For example, other rights such as publicity, privacy, or moral rights may limit how you use the material.
28 |
29 |
30 | Code
31 | --------
32 |
33 | **SparkFun code, firmware, and software are released under the [MIT License](http://opensource.org/licenses/MIT).**
34 |
35 | The MIT License (MIT)
36 |
37 | Copyright (c) 2020 SparkFun Electronics
38 |
39 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
40 |
41 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
42 |
43 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
44 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | SparkFun Weather Meter Kit Arduino Library
2 | ========================================
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | 
16 |
17 | [Weather Meter Kit](https://www.sparkfun.com/products/15901) *(SEN-15901)*
18 |
19 |
20 | Whether you're an agriculturalist, a professional meteorologist, or a weather hobbyist, building a weather station can be a rewarding project. This Arduino library allows users to easily record wind speed, wind direction, and rainfall data from our [weather meter kit](https://www.sparkfun.com/products/15901). It should be noted that the sensors in the weather meter kit rely on magnetic reed switches and requires a power source to make any measurements.
21 |
22 | - The rain gauge is a self-emptying bucket-type rain gauge, which activates a momentary button closure for each 0.011" of rain that is collected.
23 | - The anemometer (wind speed meter) encodes the wind speed by simply closing a switch which each rotation. A wind speed of 1.492 MPH produces a switch closure once per second.
24 | - The wind vane reports wind direction as a voltage, which is produced by the combination of resistors inside the sensor. The vane’s magnet could potentially close two switches at once, allowing up to 16 different positions to be indicated, but we have found that 8 positions are more realistic.
25 |
26 |
27 | SparkFun labored with love to create this code. Feel like supporting open-source hardware and software? Buy a board from SparkFun!
28 | *This library is intended to be utilized with the [weather meter kit](https://www.sparkfun.com/products/15901) and the following boards:*
29 |
30 |
31 |
32 |  |
33 |  |
34 |
35 |
36 | SparkFun Weather Shield [DEV-13956] |
37 | SparkFun MicroMod Weather Carrier Board [SEN-16794] |
38 |
39 |
40 |
41 | ## Supported Microcontrollers - Arduino Environment
42 |
43 | * Weather Shield
44 | * [ATMega328](https://www.sparkfun.com/products/18158)
45 | * MicroMod Weather Carrier Board
46 | * [Artemis](https://www.sparkfun.com/products/16401)
47 | * [SAMD51](https://www.sparkfun.com/products/16791)
48 | * [ESP32](https://www.sparkfun.com/products/16781)
49 | * [STM32](https://www.sparkfun.com/products/21326)
50 | * [nrf5280](https://www.sparkfun.com/products/16984)
51 | * [Teensy](https://www.sparkfun.com/products/16402)
52 | * [RP2040](https://www.sparkfun.com/products/17720)
53 |
54 | Repository Contents
55 | -------------------
56 |
57 | * [**/documents**](./documents) - Datasheet and User Manual
58 | * [**/examples**](./examples) - Example sketches for the library (.ino). Run these from the Arduino IDE.
59 | * [**/src**](./src) - Source files for the library (.cpp, .h).
60 | * [**keywords.txt**](./keywords.txt) - Keywords from this library that will be highlighted in the Arduino IDE.
61 | * [**library.properties**](./library.properties) - General library properties for the Arduino package manager.
62 | * [**CONTRIBUTING.md**](./CONTRIBUTING.md) - Guidelines on how to contribute to this library.
63 |
64 | Documentation
65 | -------------
66 |
67 | * **[Installing an Arduino Library Guide](https://learn.sparkfun.com/tutorials/installing-an-arduino-library)** - Basic information on how to install an Arduino library
68 | * **[Assembly Guide](https://learn.sparkfun.com/tutorials/681)** - A tutorial for assembling the weather meter kit
69 |
70 |
71 | Products that use this Library
72 | ------------------------------
73 |
74 | * **[SEN-16794](https://www.sparkfun.com/products/16794)** - SparkFun MicroMod Weather Carrier Board
75 | * **[DEV-13956](https://www.sparkfun.com/products/13956)** - SparkFun Weather Shield
76 | * **[SEN-15901](https://www.sparkfun.com/products/15901)** - Weather Meter Kit
77 |
78 | Contributing
79 | ------------
80 | If you would like to contribute to this library: please do, we truly appreciate it, but please follow [these guidelines](./CONTRIBUTING.md). Thanks!
81 |
82 | License Information
83 | -------------------
84 |
85 | SparkFun's source files are ***open source***!
86 |
87 | Please review the [`LICENSE.md`](LICENSE.md) file for license information.
88 |
89 | Distributed as-is; no warranty is given.
90 |
91 | \- Your friends at SparkFun.
92 |
--------------------------------------------------------------------------------
/documents/Weather_Meter_Kit_Datasheet.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sparkfun/SparkFun_Weather_Meter_Kit_Arduino_Library/ed0488bd4a5bcd666c0af17d8a7d4592b221434d/documents/Weather_Meter_Kit_Datasheet.pdf
--------------------------------------------------------------------------------
/examples/Example1_BasicReadings/Example1_BasicReadings.ino:
--------------------------------------------------------------------------------
1 | #include "SparkFun_Weather_Meter_Kit_Arduino_Library.h"
2 |
3 | // Below are the pin definitions for each sensor of the weather meter kit
4 |
5 | // Pins for Weather Carrier with ESP32 Processor Board
6 | int windDirectionPin = 35;
7 | int windSpeedPin = 14;
8 | int rainfallPin = 27;
9 |
10 | // Pins for the Weather Shield with SparkFun RedBoard Qwiic or Arduino Uno
11 | // int windDirectionPin = A0;
12 | // int windSpeedPin = 3;
13 | // int rainfallPin = 2;
14 |
15 | // Create an instance of the weather meter kit
16 | SFEWeatherMeterKit weatherMeterKit(windDirectionPin, windSpeedPin, rainfallPin);
17 |
18 | void setup()
19 | {
20 | // Begin serial
21 | Serial.begin(115200);
22 | Serial.println(F("SparkFun Weather Meter Kit Example 1 - Basic Readings"));
23 | Serial.println();
24 | Serial.println(F("Note - this example demonstrates the minimum code required"));
25 | Serial.println(F("for operation, and may not be accurate for your project."));
26 | Serial.println(F("It is recommended to check out the calibration examples."));
27 |
28 | // Expected ADC values have been defined for various platforms in the
29 | // library, however your platform may not be included. This code will check
30 | // if that's the case
31 | #ifdef SFE_WMK_PLAFTORM_UNKNOWN
32 | // The platform you're using hasn't been added to the library, so the
33 | // expected ADC values have been calculated assuming a 10k pullup resistor
34 | // and a perfectly linear 16-bit ADC. Your ADC likely has a different
35 | // resolution, so you'll need to specify it here:
36 | weatherMeterKit.setADCResolutionBits(10);
37 |
38 | Serial.println(F("Unknown platform! Please edit the code with your ADC resolution!"));
39 | Serial.println();
40 | #endif
41 |
42 | // Begin weather meter kit
43 | weatherMeterKit.begin();
44 | }
45 |
46 | void loop()
47 | {
48 | // Print data from weather meter kit
49 | Serial.print(F("Wind direction (degrees): "));
50 | Serial.print(weatherMeterKit.getWindDirection(), 1);
51 | Serial.print(F("\t\t"));
52 | Serial.print(F("Wind speed (kph): "));
53 | Serial.print(weatherMeterKit.getWindSpeed(), 1);
54 | Serial.print(F("\t\t"));
55 | Serial.print(F("Total rainfall (mm): "));
56 | Serial.println(weatherMeterKit.getTotalRainfall(), 1);
57 |
58 | // Only print once per second
59 | delay(1000);
60 | }
61 |
--------------------------------------------------------------------------------
/examples/Example2_ManualCalibration/Example2_ManualCalibration.ino:
--------------------------------------------------------------------------------
1 | #include "SparkFun_Weather_Meter_Kit_Arduino_Library.h"
2 |
3 | // Below are the pin definitions for each sensor of the weather meter kit
4 |
5 | // Pins for Weather Carrier with ESP32 Processor Board
6 | int windDirectionPin = 35;
7 | int windSpeedPin = 14;
8 | int rainfallPin = 27;
9 |
10 | // Pins for the Weather Shield with SparkFun RedBoard Qwiic or Arduino Uno
11 | // int windDirectionPin = A0;
12 | // int windSpeedPin = 3;
13 | // int rainfallPin = 2;
14 |
15 | // Create an instance of the weather meter kit
16 | SFEWeatherMeterKit weatherMeterKit(windDirectionPin, windSpeedPin, rainfallPin);
17 |
18 | void setup()
19 | {
20 | // Begin serial
21 | Serial.begin(115200);
22 | Serial.println(F("SparkFun Weather Meter Kit Example 2 - Manual Calibration"));
23 | Serial.println();
24 | Serial.println(F("Note - this example demonstrates how to manually set the"));
25 | Serial.println(F("calibration parameters once you know what they are for your"));
26 | Serial.println(F("set up. If you don't know what values to use, check out"));
27 | Serial.println(F("Example 3, which walks you through it! The values used in"));
28 | Serial.println(F("this example are all defaults, so you may need to change them."));
29 |
30 | // Here we create a struct to hold all the calibration parameters
31 | SFEWeatherMeterKitCalibrationParams calibrationParams = weatherMeterKit.getCalibrationParams();
32 |
33 | // The wind vane has 8 switches, but 2 could close at the same time, which
34 | // results in 16 possible positions. Each position has a resistor connected
35 | // to GND, so this library assumes a voltage divider is created by adding
36 | // another resistor to VCC. Some of the wind vane resistor values are
37 | // fairly close to each other, meaning an accurate ADC is required. However
38 | // some ADCs have a non-linear behavior that causes this measurement to be
39 | // inaccurate. To account for this, the vane resistor values can be manually
40 | // changed here to compensate for the non-linear behavior of the ADC
41 | calibrationParams.vaneADCValues[WMK_ANGLE_0_0] = 3143;
42 | calibrationParams.vaneADCValues[WMK_ANGLE_22_5] = 1624;
43 | calibrationParams.vaneADCValues[WMK_ANGLE_45_0] = 1845;
44 | calibrationParams.vaneADCValues[WMK_ANGLE_67_5] = 335;
45 | calibrationParams.vaneADCValues[WMK_ANGLE_90_0] = 372;
46 | calibrationParams.vaneADCValues[WMK_ANGLE_112_5] = 264;
47 | calibrationParams.vaneADCValues[WMK_ANGLE_135_0] = 738;
48 | calibrationParams.vaneADCValues[WMK_ANGLE_157_5] = 506;
49 | calibrationParams.vaneADCValues[WMK_ANGLE_180_0] = 1149;
50 | calibrationParams.vaneADCValues[WMK_ANGLE_202_5] = 979;
51 | calibrationParams.vaneADCValues[WMK_ANGLE_225_0] = 2520;
52 | calibrationParams.vaneADCValues[WMK_ANGLE_247_5] = 2397;
53 | calibrationParams.vaneADCValues[WMK_ANGLE_270_0] = 3780;
54 | calibrationParams.vaneADCValues[WMK_ANGLE_292_5] = 3309;
55 | calibrationParams.vaneADCValues[WMK_ANGLE_315_0] = 3548;
56 | calibrationParams.vaneADCValues[WMK_ANGLE_337_5] = 2810;
57 |
58 | // The rainfall detector contains a small cup that collects rain water. When
59 | // the cup fills, the water is dumped and the total rainfall is incremented
60 | // by some value. This value defaults to 0.2794mm of rain per count, as
61 | // specified by the datasheet
62 | calibrationParams.mmPerRainfallCount = 0.2794;
63 |
64 | // The rainfall detector switch can sometimes bounce, causing multiple extra
65 | // triggers. This input is debounced by ignoring extra triggers within a
66 | // time window, which defaults to 100ms
67 | calibrationParams.minMillisPerRainfall = 100;
68 |
69 | // The anemometer contains a switch that opens and closes as it spins. The
70 | // rate at which the switch closes depends on the wind speed. The datasheet
71 | // states that a wind of 2.4kph causes the switch to close once per second
72 | calibrationParams.kphPerCountPerSec = 2.4;
73 |
74 | // Because the anemometer generates discrete pulses as it rotates, it's not
75 | // possible to measure the wind speed exactly at any point in time. A filter
76 | // is implemented in the library that averages the wind speed over a certain
77 | // time period, which defaults to 1 second. Longer intervals result in more
78 | // accurate measurements, but cause delay in the measurement
79 | calibrationParams.windSpeedMeasurementPeriodMillis = 1000;
80 |
81 | // Now we can set all the calibration parameters at once
82 | weatherMeterKit.setCalibrationParams(calibrationParams);
83 |
84 | // Begin weather meter kit
85 | weatherMeterKit.begin();
86 | }
87 |
88 | void loop()
89 | {
90 | // Print data from weather meter kit
91 | Serial.print(F("Wind direction (degrees): "));
92 | Serial.print(weatherMeterKit.getWindDirection(), 1);
93 | Serial.print(F("\t\t"));
94 | Serial.print(F("Wind speed (kph): "));
95 | Serial.print(weatherMeterKit.getWindSpeed(), 1);
96 | Serial.print(F("\t\t"));
97 | Serial.print(F("Total rainfall (mm): "));
98 | Serial.println(weatherMeterKit.getTotalRainfall(), 1);
99 |
100 | // Only print once per second
101 | delay(1000);
102 | }
--------------------------------------------------------------------------------
/examples/Example3_CalibrationHelper/Example3_CalibrationHelper.ino:
--------------------------------------------------------------------------------
1 | #include "SparkFun_Weather_Meter_Kit_Arduino_Library.h"
2 |
3 | // Below are the pin definitions for each sensor of the weather meter kit
4 |
5 | // Pins for Weather Carrier with ESP32 Processor Board
6 | int windDirectionPin = 35;
7 | int windSpeedPin = 14;
8 | int rainfallPin = 27;
9 |
10 | // Pins for the Weather Shield with SparkFun RedBoard Qwiic or Arduino Uno
11 | // int windDirectionPin = A0;
12 | // int windSpeedPin = 3;
13 | // int rainfallPin = 2;
14 |
15 | // Create an instance of the weather meter kit
16 | SFEWeatherMeterKit weatherMeterKit(windDirectionPin, windSpeedPin, rainfallPin);
17 |
18 | // Here we create a struct to hold all the calibration parameters
19 | SFEWeatherMeterKitCalibrationParams calibrationParams;
20 |
21 | void setup()
22 | {
23 | // Begin serial
24 | Serial.begin(115200);
25 | Serial.println(F("SparkFun Weather Meter Kit Example 3 - Calibration Helper"));
26 | Serial.println();
27 | Serial.println(F("This example will help you determine the best calibration"));
28 | Serial.println(F("parameters to use for your project. Once each section is done,"));
29 | Serial.println(F("the values will be printed for you to copy into your sketch."));
30 |
31 | // We'll be changing the calibration parameters one at a time, so we'll get
32 | // all the default values now
33 | calibrationParams = weatherMeterKit.getCalibrationParams();
34 |
35 | // Begin weather meter kit
36 | weatherMeterKit.begin();
37 |
38 | // Run the calibration helper
39 | runCalibrationHelper();
40 |
41 | Serial.println();
42 | Serial.println(F("Calibration done! Enter any key to continue"));
43 | waitForUserInput();
44 | }
45 |
46 | void loop()
47 | {
48 | // Print data from weather meter kit
49 | Serial.print(F("Wind direction (degrees): "));
50 | Serial.print(weatherMeterKit.getWindDirection(), 1);
51 | Serial.print(F("\t\t"));
52 | Serial.print(F("Wind speed (kph): "));
53 | Serial.print(weatherMeterKit.getWindSpeed(), 1);
54 | Serial.print(F("\t\t"));
55 | Serial.print(F("Total rainfall (mm): "));
56 | Serial.println(weatherMeterKit.getTotalRainfall(), 1);
57 |
58 | // Only print once per second
59 | delay(1000);
60 | }
61 |
62 | void runCalibrationHelper()
63 | {
64 | // Run the helpers for each sensor
65 | runVaneCalibrationHelper();
66 | runRainfallCalibrationHelper();
67 | runAnemometerCalibrationHelper();
68 | }
69 |
70 | void runVaneCalibrationHelper()
71 | {
72 | Serial.println();
73 | Serial.println(F("Wind vane calibration!"));
74 |
75 | Serial.println();
76 | Serial.println(F("The wind vane has several switches, each with different"));
77 | Serial.println(F("resistors connected to GND. This library assumes there's an"));
78 | Serial.println(F("external resistor connected to VCC creating a voltage divider;"));
79 | Serial.println(F("the voltage is measured and compared with expected voltages"));
80 | Serial.println(F("for each direction. The expected voltages may need to be tuned,"));
81 | Serial.println(F("which this part walks you through. Hold the wind vane at the"));
82 | Serial.println(F("specified angle, then enter any key once steady. Pay close"));
83 | Serial.println(F("attention to the measured ADC value to see when it changes,"));
84 | Serial.println(F("especially around the 22.5 degree increments, they're very"));
85 | Serial.println(F("narrow! Enter any key to begin."));
86 |
87 | // Wait for user to begin
88 | waitForUserInput();
89 |
90 | // Loop through all angles
91 | for (int i = 0; i < WMK_NUM_ANGLES; i++)
92 | {
93 | // Compute current angle
94 | float currentAngle = i * SFE_WIND_VANE_DEGREES_PER_INDEX;
95 |
96 | // Loop until user requests to continue
97 | clearUserInput();
98 | while (Serial.available() == 0)
99 | {
100 | Serial.print(F("Hold wind vane at "));
101 | Serial.print(currentAngle, 1);
102 | Serial.print(F(" degrees. Enter any key when in position."));
103 | Serial.print(F(" Measured ADC: "));
104 | Serial.print(analogRead(windDirectionPin));
105 | Serial.print(F(" Measured direction (degrees): "));
106 | Serial.println(weatherMeterKit.getWindDirection(), 1);
107 |
108 | // Print moderately quickly so user can catch any brief changes
109 | delay(100);
110 | }
111 |
112 | // Set this as the new expected ADC value for this angle
113 | uint32_t measuredADC = analogRead(windDirectionPin);
114 | calibrationParams.vaneADCValues[i] = measuredADC;
115 | weatherMeterKit.setCalibrationParams(calibrationParams);
116 |
117 | // Print value for user to see
118 | Serial.println();
119 | Serial.print(F("Setting expected ADC value for "));
120 | Serial.print(currentAngle);
121 | Serial.print(F(" degrees to "));
122 | Serial.println(measuredADC);
123 | Serial.println();
124 |
125 | // Wait a bit so user can read it
126 | delay(1000);
127 | }
128 |
129 | // Print the ADC value saved for each angle again so the user has it all in
130 | // one place
131 | Serial.println();
132 | Serial.println(F("Here are the ADC values set for each angle:"));
133 | Serial.println();
134 | for (int i = 0; i < WMK_NUM_ANGLES; i++)
135 | {
136 | // Compute current angle
137 | float currentAngle = i * SFE_WIND_VANE_DEGREES_PER_INDEX;
138 |
139 | // Print this angle / ADC pair
140 | Serial.print(currentAngle, 1);
141 | Serial.print(F(" degrees: "));
142 | Serial.println(calibrationParams.vaneADCValues[i]);
143 | }
144 |
145 | Serial.println();
146 | Serial.println(F("Wind vane calibration complete!"));
147 | }
148 |
149 | void runRainfallCalibrationHelper()
150 | {
151 | Serial.println();
152 | Serial.println(F("Rainfall calibration!"));
153 |
154 | // Rainfall calibration
155 | Serial.println();
156 | Serial.println(F("The rainfall detector contains a small cup that collects rain"));
157 | Serial.println(F("water. When the cup fills, the water gets dumped out and a"));
158 | Serial.println(F("counter is incremented. The exact volume of this cup needs to"));
159 | Serial.println(F("be known to get an accurate measurement of the total rainfall."));
160 | Serial.println(F("To calibrate this value, you'll need to pour a known volume"));
161 | Serial.println(F("of water into the rainfall detector, and the cup volume will"));
162 | Serial.println(F("be calculated. The rate at which the water is poured can"));
163 | Serial.println(F("affect the measurement, so go very slowly to simulate actual"));
164 | Serial.println(F("rain rather than dumping it all at once!"));
165 | Serial.println(F("Enter any key once you're ready to begin"));
166 |
167 | // Wait for user to begin
168 | waitForUserInput();
169 |
170 | // User is ready, reset the rainfall counter
171 | weatherMeterKit.resetTotalRainfall();
172 |
173 | Serial.println();
174 | Serial.println(F("Begin pouring!"));
175 | Serial.println();
176 |
177 | // Wait for user to finish
178 | clearUserInput();
179 | while (Serial.available() == 0)
180 | {
181 | Serial.print(F("Enter any key once finished pouring."));
182 | Serial.print(F(" Number of counts: "));
183 | Serial.print(weatherMeterKit.getRainfallCounts());
184 | Serial.print(F(" Measured rainfall (mm): "));
185 | Serial.println(weatherMeterKit.getTotalRainfall(), 1);
186 |
187 | // Print slowly
188 | delay(1000);
189 | }
190 |
191 | Serial.println();
192 | Serial.println(F("Now enter the volume of water poured in mL"));
193 | waitForUserInput();
194 | int totalWaterML = Serial.parseInt();
195 |
196 | // Convert ml to mm^3
197 | int totalWaterMM3 = totalWaterML * 1000;
198 |
199 | // Divide by collection area of rainfall detector. It's about 50mm x 110mm,
200 | // resulting in a collection area of about 5500mm^2
201 | float totalRainfallMM = totalWaterMM3 / 5500.0;
202 |
203 | // Divide by number of counts
204 | float mmPerCount = totalRainfallMM / weatherMeterKit.getRainfallCounts();
205 |
206 | // Set this as the new mm per count
207 | calibrationParams.mmPerRainfallCount = mmPerCount;
208 | weatherMeterKit.setCalibrationParams(calibrationParams);
209 |
210 | // Print value for user to see
211 | Serial.println();
212 | Serial.print(F("Setting mm per count to "));
213 | Serial.println(mmPerCount, 4);
214 |
215 | Serial.println();
216 | Serial.println(F("Rainfall calibration complete!"));
217 | }
218 |
219 | void runAnemometerCalibrationHelper()
220 | {
221 | Serial.println();
222 | Serial.println(F("Anemometer calibration!"));
223 |
224 | Serial.println();
225 | Serial.println(F("This part will require you to place the anemometer in a"));
226 | Serial.println(F("constant wind stream for a few seconds, and you'll need to"));
227 | Serial.println(F("know or the wind speed or measure it with a calibrated"));
228 | Serial.println(F("anemometer (these can be purchased for relatively low cost)."));
229 | Serial.println(F("Enter the number of seconds you wish to run this calibration."));
230 | Serial.println(F("Longer will be more accurate, but the wind speed is more"));
231 | Serial.println(F("likely to fluctuate (10 seconds is recommended)"));
232 | waitForUserInput();
233 | int calibrationSeconds = Serial.parseInt();
234 |
235 | // Set filter measurement period as requested
236 | calibrationParams.windSpeedMeasurementPeriodMillis = 1000 * calibrationSeconds;
237 | weatherMeterKit.setCalibrationParams(calibrationParams);
238 |
239 | Serial.println();
240 | Serial.println(F("Now place the anemometer in a constant wind stream, and"));
241 | Serial.println(F("enter any key when ready to begin calibration"));
242 | waitForUserInput();
243 |
244 | // Reset the wind speed filter to start the calibration period
245 | weatherMeterKit.resetWindSpeedFilter();
246 |
247 | // Wait for calibration period to end
248 | Serial.println();
249 | for(int i = 0; i < calibrationSeconds; i++)
250 | {
251 | // Print time remaining
252 | Serial.print(F("Seconds remaining: "));
253 | Serial.println(calibrationSeconds - i);
254 |
255 | // 1 second intervals
256 | delay(1000);
257 | }
258 |
259 | // Wait just a bit longer to make sure the filter window has passed
260 | delay(500);
261 |
262 | // Store total number of wind speed counts
263 | uint32_t windCounts = weatherMeterKit.getWindSpeedCounts();
264 |
265 | // Reset measurement period back to default
266 | calibrationParams.windSpeedMeasurementPeriodMillis = 1000;
267 |
268 | Serial.println();
269 | Serial.println(F("Calibration period finished! Enter the average wind speed"));
270 | Serial.println(F("during the calibration period in kph"));
271 | waitForUserInput();
272 | float windSpeed = Serial.parseFloat();
273 |
274 | // Calculate kph per count per second
275 | calibrationParams.kphPerCountPerSec = windSpeed * windCounts / calibrationSeconds;
276 | weatherMeterKit.setCalibrationParams(calibrationParams);
277 |
278 | // Print value for user to see
279 | Serial.println();
280 | Serial.print(F("Setting kph per count per second to "));
281 | Serial.println(calibrationParams.kphPerCountPerSec, 2);
282 |
283 | Serial.println();
284 | Serial.println(F("Anemometer calibration complete!"));
285 | }
286 |
287 | void clearUserInput()
288 | {
289 | // Ensure all previous characters have come through
290 | delay(100);
291 |
292 | // Throw away all previous characters
293 | while (Serial.available() != 0)
294 | {
295 | Serial.read();
296 | }
297 | }
298 |
299 | void waitForUserInput()
300 | {
301 | // Remove previous user input
302 | clearUserInput();
303 |
304 | // Wait for user to input something
305 | while (Serial.available() == 0)
306 | {
307 | // Nothing to do, keep waiting
308 | }
309 | }
--------------------------------------------------------------------------------
/keywords.txt:
--------------------------------------------------------------------------------
1 | #########################################################
2 | # Syntax Coloring Map for SparkFun Weather Meter Kit #
3 | #########################################################
4 | # Class
5 | #########################################################
6 |
7 | SFEWeatherMeterKit KEYWORD1
8 |
9 | #########################################################
10 | # Methods and Functions
11 | #########################################################
12 |
13 | begin KEYWORD2
14 | getWindDirection KEYWORD2
15 | getWindSpeed KEYWORD2
16 | getTotalRainfall KEYWORD2
17 | getCalibrationParams KEYWORD2
18 | setCalibrationParams KEYWORD2
19 | setADCResolutionBits KEYWORD2
20 | getWindSpeedCounts KEYWORD2
21 | getRainfallCounts KEYWORD2
22 | resetWindSpeedFilter KEYWORD2
23 | resetTotalRainfall KEYWORD2
24 |
25 | #########################################################
26 | # Constants
27 | #########################################################
28 |
29 | WMK_ANGLE_0_0 LITERAL1
30 | WMK_ANGLE_22_5 LITERAL1
31 | WMK_ANGLE_45_0 LITERAL1
32 | WMK_ANGLE_67_5 LITERAL1
33 | WMK_ANGLE_90_0 LITERAL1
34 | WMK_ANGLE_112_5 LITERAL1
35 | WMK_ANGLE_135_0 LITERAL1
36 | WMK_ANGLE_157_5 LITERAL1
37 | WMK_ANGLE_180_0 LITERAL1
38 | WMK_ANGLE_202_5 LITERAL1
39 | WMK_ANGLE_225_0 LITERAL1
40 | WMK_ANGLE_247_5 LITERAL1
41 | WMK_ANGLE_270_0 LITERAL1
42 | WMK_ANGLE_292_5 LITERAL1
43 | WMK_ANGLE_315_0 LITERAL1
44 | WMK_ANGLE_337_5 LITERAL1
45 | WMK_NUM_ANGLES LITERAL1
46 | SFE_WIND_VANE_DEGREES_PER_INDEX LITERAL1
47 | SFE_WIND_VANE_ADC_RESOLUTION_DEFAULT LITERAL1
48 |
49 | #########################################################
50 | # Structs
51 | #########################################################
52 |
53 | SFEWeatherMeterKitCalibrationParams LITERAL3
--------------------------------------------------------------------------------
/library.properties:
--------------------------------------------------------------------------------
1 | name=SparkFun Weather Meter Kit Arduino Library
2 | version=1.1.2
3 | author=SparkFun Electronics
4 | maintainer=SparkFun Electronics
5 | sentence=A library to use the SparkFun Weather Meter Kit
6 | paragraph=
7 | category=Sensors
8 | url=https://github.com/sparkfun/SparkFun_Weather_Meter_Kit_Arduino_Library
9 | architectures=*
10 |
--------------------------------------------------------------------------------
/src/SparkFun_Weather_Meter_Kit_Arduino_Library.cpp:
--------------------------------------------------------------------------------
1 | #include "SparkFun_Weather_Meter_Kit_Arduino_Library.h"
2 |
3 | // Static member definitions
4 | SFEWeatherMeterKitCalibrationParams SFEWeatherMeterKit::_calibrationParams;
5 | uint32_t SFEWeatherMeterKit::_windCountsPrevious;
6 | uint32_t SFEWeatherMeterKit::_windCounts;
7 | uint32_t SFEWeatherMeterKit::_rainfallCounts;
8 | uint32_t SFEWeatherMeterKit::_lastWindSpeedMillis;
9 | uint32_t SFEWeatherMeterKit::_lastRainfallMillis;
10 | uint8_t SFEWeatherMeterKit::_windDirectionPin;
11 | uint8_t SFEWeatherMeterKit::_windSpeedPin;
12 | uint8_t SFEWeatherMeterKit::_rainfallPin;
13 |
14 | /// @brief Default constructor, sets default calibration values
15 | SFEWeatherMeterKit::SFEWeatherMeterKit(uint8_t windDirectionPin, uint8_t windSpeedPin, uint8_t rainfallPin)
16 | {
17 | // Set sensors pins
18 | _windDirectionPin = windDirectionPin;
19 | _windSpeedPin = windSpeedPin;
20 | _rainfallPin = rainfallPin;
21 |
22 | // The wind vane has 8 switches, but 2 could close at the same time, which
23 | // results in 16 possible positions. Each position has a different resistor,
24 | // resulting in different ADC values. The expected ADC values has been
25 | // experiemntally determined for various platforms, see the constants file
26 | _calibrationParams.vaneADCValues[WMK_ANGLE_0_0] = SFE_WMK_ADC_ANGLE_0_0;
27 | _calibrationParams.vaneADCValues[WMK_ANGLE_22_5] = SFE_WMK_ADC_ANGLE_22_5;
28 | _calibrationParams.vaneADCValues[WMK_ANGLE_45_0] = SFE_WMK_ADC_ANGLE_45_0;
29 | _calibrationParams.vaneADCValues[WMK_ANGLE_67_5] = SFE_WMK_ADC_ANGLE_67_5;
30 | _calibrationParams.vaneADCValues[WMK_ANGLE_90_0] = SFE_WMK_ADC_ANGLE_90_0;
31 | _calibrationParams.vaneADCValues[WMK_ANGLE_112_5] = SFE_WMK_ADC_ANGLE_112_5;
32 | _calibrationParams.vaneADCValues[WMK_ANGLE_135_0] = SFE_WMK_ADC_ANGLE_135_0;
33 | _calibrationParams.vaneADCValues[WMK_ANGLE_157_5] = SFE_WMK_ADC_ANGLE_157_5;
34 | _calibrationParams.vaneADCValues[WMK_ANGLE_180_0] = SFE_WMK_ADC_ANGLE_180_0;
35 | _calibrationParams.vaneADCValues[WMK_ANGLE_202_5] = SFE_WMK_ADC_ANGLE_202_5;
36 | _calibrationParams.vaneADCValues[WMK_ANGLE_225_0] = SFE_WMK_ADC_ANGLE_225_0;
37 | _calibrationParams.vaneADCValues[WMK_ANGLE_247_5] = SFE_WMK_ADC_ANGLE_247_5;
38 | _calibrationParams.vaneADCValues[WMK_ANGLE_270_0] = SFE_WMK_ADC_ANGLE_270_0;
39 | _calibrationParams.vaneADCValues[WMK_ANGLE_292_5] = SFE_WMK_ADC_ANGLE_292_5;
40 | _calibrationParams.vaneADCValues[WMK_ANGLE_315_0] = SFE_WMK_ADC_ANGLE_315_0;
41 | _calibrationParams.vaneADCValues[WMK_ANGLE_337_5] = SFE_WMK_ADC_ANGLE_337_5;
42 |
43 | // Datasheet specifies 2.4kph of wind causes one trigger per second
44 | _calibrationParams.kphPerCountPerSec = 2.4;
45 |
46 | // Wind speed sampling interval. Longer durations have more accuracy, but
47 | // cause delay and can miss fast fluctuations
48 | _calibrationParams.windSpeedMeasurementPeriodMillis = 1000;
49 |
50 | // Datasheet specifies 0.2794mm of rain per trigger
51 | _calibrationParams.mmPerRainfallCount = 0.2794;
52 |
53 | // Debounce time for rainfall detector
54 | _calibrationParams.minMillisPerRainfall = 100;
55 |
56 | // Reset counters to zero
57 | _windCountsPrevious = 0;
58 | _windCounts = 0;
59 | _rainfallCounts = 0;
60 |
61 | // Reset timers
62 | _lastWindSpeedMillis = millis();
63 | _lastRainfallMillis = millis();
64 | }
65 |
66 | /// @brief Sets up sensor pins
67 | /// @param windDirectionPin Wind direction pin, must have an ADC
68 | /// @param windSpeedPin Wind speed pin, must support interrupts
69 | /// @param rainfallPin Rainfall pin, must support interrupts
70 | void SFEWeatherMeterKit::begin()
71 | {
72 | // Set pins to inputs
73 | pinMode(_windDirectionPin, INPUT);
74 | pinMode(_windSpeedPin, INPUT_PULLUP);
75 | pinMode(_rainfallPin, INPUT_PULLUP);
76 |
77 | // Attach interrupt handlers
78 | attachInterrupt(digitalPinToInterrupt(_windSpeedPin), windSpeedInterrupt, CHANGE);
79 | attachInterrupt(digitalPinToInterrupt(_rainfallPin), rainfallInterrupt, RISING);
80 | }
81 |
82 | /// @brief Gets the current calibration parameters
83 | /// @return Current calibration parameters
84 | SFEWeatherMeterKitCalibrationParams SFEWeatherMeterKit::getCalibrationParams()
85 | {
86 | return _calibrationParams;
87 | }
88 |
89 | /// @brief Sets the new calibration parameters
90 | /// @param params New calibration parameters
91 | void SFEWeatherMeterKit::setCalibrationParams(SFEWeatherMeterKitCalibrationParams params)
92 | {
93 | // Copy the provided calibration parameters
94 | memcpy(&_calibrationParams, ¶ms, sizeof(SFEWeatherMeterKitCalibrationParams));
95 | }
96 |
97 | /// @brief Adjusts the expected ADC values for the wind vane based on the
98 | /// provided ADC resolution
99 | /// @param resolutionBits Resolution of ADC in bits (eg. 8-bit, 12-bit, etc.)
100 | void SFEWeatherMeterKit::setADCResolutionBits(uint8_t resolutionBits)
101 | {
102 | for(uint8_t i = 0; i < WMK_NUM_ANGLES; i++)
103 | {
104 | int8_t bitShift = (SFE_WMK_ADC_RESOLUTION) - resolutionBits;
105 |
106 | if(bitShift > 0)
107 | {
108 | _calibrationParams.vaneADCValues[i] >>= bitShift;
109 | }
110 | else if(bitShift < 0)
111 | {
112 | _calibrationParams.vaneADCValues[i] <<= -bitShift;
113 | }
114 | }
115 | }
116 |
117 | /// @brief Measures the direction of the wind vane
118 | /// @return Wind direction in degrees
119 | float SFEWeatherMeterKit::getWindDirection()
120 | {
121 | // Measure the output of the voltage divider
122 | uint16_t rawADC = analogRead(_windDirectionPin);
123 |
124 | // Now we'll loop through all possible directions to find which is closest
125 | // to our measurement, using a simple linear search. closestDifference is
126 | // initialized to max 16-bit signed value (2^15 - 1 = 32,767)
127 | int16_t closestDifference = 32767;
128 | uint8_t closestIndex = 0;
129 | for (uint8_t i = 0; i < WMK_NUM_ANGLES; i++)
130 | {
131 | // Compute the difference between the ADC value for this direction and
132 | // what we measured
133 | int16_t adcDifference = _calibrationParams.vaneADCValues[i] - rawADC;
134 |
135 | // We only care about the magnitude of the difference
136 | adcDifference = abs(adcDifference);
137 |
138 | // Check if this different is less than our closest so far
139 | if (adcDifference < closestDifference)
140 | {
141 | // This resistance is closer, update closest resistance and index
142 | closestDifference = adcDifference;
143 | closestIndex = i;
144 | }
145 | }
146 |
147 | // Now compute the wind direction in degrees
148 | float direction = closestIndex * SFE_WIND_VANE_DEGREES_PER_INDEX;
149 |
150 | // Return direction in degrees
151 | return direction;
152 | }
153 |
154 | /// @brief Updates the wind speed measurement windows if needed
155 | void SFEWeatherMeterKit::updateWindSpeed()
156 | {
157 | // The anemometer generates interrupts as it spins. Because these are
158 | // discrete pulses, we can't get an instantaneous measurement of the wind
159 | // speed. Instead, we need to track these signals over time and perform some
160 | // filtering to get an estimate of the current wind speed. There's lots of
161 | // ways to do this, but this library uses a modifed version of a moving
162 | // window filter.
163 | //
164 | // A moving window filter would require an array of values to be stored,
165 | // indicating when each pulse occurred. However for a fixed time window, the
166 | // number of pulses is unknown, so we don't know how big the array needs to
167 | // be. There are some solutions to this, but the one used here is to change
168 | // the moving time window to a static time window, which is illustrated in
169 | // this timing diagram with variable time between pulses:
170 | //
171 | // Pulses | | | | | | | | |
172 | // Window Last window Current window
173 | // Time ------|-----------------------|----------------|
174 | // t_last t_now
175 | // |---Measurement Period--|---Measurement Period--|
176 | //
177 | // A counter is used to track the number of pulses detected in the current
178 | // measurement window; when pulses are detected, the counter is incremented.
179 | // When t_now exceeds the measurement period, the total number of pulses is
180 | // used to calculate the average wind speed for that window. This filter
181 | // only outputs wind speed for the previous window, which does result in
182 | // delayed measurements, but is fine for most data logging applications since
183 | // logs can be synced with the measurement widows
184 |
185 | // Get current time
186 | uint32_t tNow = millis();
187 |
188 | // Compute time since start of current measurement window
189 | uint32_t dt = tNow - _lastWindSpeedMillis;
190 |
191 | // Check how long it's been since the start of this measurement window
192 | if (dt < _calibrationParams.windSpeedMeasurementPeriodMillis)
193 | {
194 | // Still within the current window, nothing to do (count is not
195 | // incremented here, that's done by the interrupt handler)
196 | }
197 | else
198 | {
199 | // We've passed the end of the measurement window, so we need to update
200 | // some things. But first, we need to check how long it's been since the
201 | // last time we updated, since it's possible we've not received any
202 | // pulses for a long time
203 | if (dt > (_calibrationParams.windSpeedMeasurementPeriodMillis * 2))
204 | {
205 | // Over 2 measurement periods have passed since the last update,
206 | // meaning the wind speed is very slow or even zero. So we'll reset
207 | // the wind speed and counter, and set the start of the next window
208 | // to be now
209 | _windCountsPrevious = 0;
210 | _windCounts = 0;
211 | _lastWindSpeedMillis = tNow;
212 | }
213 | else
214 | {
215 | // We've only just gone past the end of the measurement period, so
216 | // save the wind counts for the previous window, reset current
217 | // counter, and update time of start of next measurement window
218 | _windCountsPrevious = _windCounts;
219 | _windCounts = 0;
220 | _lastWindSpeedMillis += _calibrationParams.windSpeedMeasurementPeriodMillis;
221 | }
222 | }
223 | }
224 |
225 | /// @brief Gets the measured wind speed
226 | /// @return Measured wind speed in kph
227 | float SFEWeatherMeterKit::getWindSpeed()
228 | {
229 | // Check if the wind speed needs to be updated
230 | updateWindSpeed();
231 |
232 | // Calculate the wind speed for the previous window. First compute the
233 | // counts per millisecond
234 | float windSpeed = (float) _windCountsPrevious / _calibrationParams.windSpeedMeasurementPeriodMillis;
235 |
236 | // Convert milliseconds to seconds, and counts per second to kph. Need to
237 | // divide by 2 to account for using both rising and falling edges
238 | windSpeed *= 1000 * _calibrationParams.kphPerCountPerSec / 2;
239 |
240 | // Return wind speed for the previous measurement interval
241 | return windSpeed;
242 | }
243 |
244 | /// @brief Gets the number of wind speed counts
245 | /// @return Number of wind speed counts
246 | uint32_t SFEWeatherMeterKit::getWindSpeedCounts()
247 | {
248 | // Return total wind speed counts
249 | return _windCounts;
250 | }
251 |
252 | /// @brief Gets the number of rainfall counts
253 | /// @return Number of rainfall counts
254 | uint32_t SFEWeatherMeterKit::getRainfallCounts()
255 | {
256 | // Return total rainfall counts
257 | return _rainfallCounts;
258 | }
259 |
260 | /// @brief Gets the total rainfall
261 | /// @return Total rainfall in mm
262 | float SFEWeatherMeterKit::getTotalRainfall()
263 | {
264 | // Return total rainfall in mm
265 | return _rainfallCounts * _calibrationParams.mmPerRainfallCount;
266 | }
267 |
268 | /// @brief Resets the wind speed
269 | void SFEWeatherMeterKit::resetWindSpeedFilter()
270 | {
271 | _windCountsPrevious = 0;
272 | _windCounts = 0;
273 | _lastWindSpeedMillis = millis();
274 | }
275 |
276 | /// @brief Resets the total rainfall
277 | void SFEWeatherMeterKit::resetTotalRainfall()
278 | {
279 | _rainfallCounts = 0;
280 | }
281 |
282 | /// @brief Interrupt handler for wind speed pin
283 | void SFEWeatherMeterKit::windSpeedInterrupt()
284 | {
285 | // Check if the measurement window needs to be updated
286 | updateWindSpeed();
287 |
288 | // Increment counts in this measurement window
289 | _windCounts++;
290 | }
291 |
292 | /// @brief Interrupt handler for rainfall pin
293 | void SFEWeatherMeterKit::rainfallInterrupt()
294 | {
295 | // Debounce by checking time since last interrupt
296 | if ((millis() - _lastRainfallMillis) < _calibrationParams.minMillisPerRainfall)
297 | {
298 | // There's not been enough time since the last interrupt, so this is
299 | // likely just the switch bouncing
300 | return;
301 | }
302 |
303 | // Enough time has passed that this is probably a real signal instead of a
304 | // bounce, so update the time of the last interrupt to be now
305 | _lastRainfallMillis = millis();
306 |
307 | // Increment counter
308 | _rainfallCounts++;
309 | }
--------------------------------------------------------------------------------
/src/SparkFun_Weather_Meter_Kit_Arduino_Library.h:
--------------------------------------------------------------------------------
1 | #ifndef __SPARKFUN_WEATHER_METER_KIT_H__
2 | #define __SPARKFUN_WEATHER_METER_KIT_H__
3 |
4 | #include "Arduino.h"
5 | #include "SparkFun_Weather_Meter_Kit_Constants.h"
6 |
7 | // Calibration parameters for each sensor
8 | struct SFEWeatherMeterKitCalibrationParams
9 | {
10 | // Wind vane
11 | uint16_t vaneADCValues[WMK_NUM_ANGLES];
12 |
13 | // Wind speed
14 | uint32_t windSpeedMeasurementPeriodMillis;
15 | float kphPerCountPerSec;
16 |
17 | // Rainfall
18 | float mmPerRainfallCount;
19 | uint32_t minMillisPerRainfall;
20 | };
21 |
22 | class SFEWeatherMeterKit
23 | {
24 | public:
25 | // Constructor
26 | SFEWeatherMeterKit(uint8_t windDirectionPin, uint8_t windSpeedPin, uint8_t rainfallPin);
27 | static void begin();
28 |
29 | // Data collection
30 | static float getWindDirection();
31 | static float getWindSpeed();
32 | static float getTotalRainfall();
33 |
34 | // Sensor calibration params
35 | static SFEWeatherMeterKitCalibrationParams getCalibrationParams();
36 | static void setCalibrationParams(SFEWeatherMeterKitCalibrationParams params);
37 |
38 | // ADC resolution scaling
39 | static void setADCResolutionBits(uint8_t resolutionBits);
40 |
41 | // Helper functions. These can be helpful for sensor calibration
42 | static uint32_t getWindSpeedCounts();
43 | static uint32_t getRainfallCounts();
44 | static void resetWindSpeedFilter();
45 | static void resetTotalRainfall();
46 |
47 | private:
48 | // Updates wind speed
49 | static void updateWindSpeed();
50 |
51 | // Interrupt handlers
52 | static void windSpeedInterrupt();
53 | static void rainfallInterrupt();
54 |
55 | // Pins for each sensor
56 | static uint8_t _windDirectionPin;
57 | static uint8_t _windSpeedPin;
58 | static uint8_t _rainfallPin;
59 |
60 | // Sensor calibration parameters
61 | static SFEWeatherMeterKitCalibrationParams _calibrationParams;
62 |
63 | // Variables to track measurements
64 | static uint32_t _windCounts;
65 | static uint32_t _windCountsPrevious;
66 | static uint32_t _rainfallCounts;
67 | static uint32_t _lastWindSpeedMillis;
68 | static uint32_t _lastRainfallMillis;
69 | };
70 |
71 | #endif
--------------------------------------------------------------------------------
/src/SparkFun_Weather_Meter_Kit_Constants.h:
--------------------------------------------------------------------------------
1 | // Enum to define the indexes for each wind direction
2 | enum SFEWeatherMeterKitAnemometerAngles
3 | {
4 | WMK_ANGLE_0_0 = 0,
5 | WMK_ANGLE_22_5,
6 | WMK_ANGLE_45_0,
7 | WMK_ANGLE_67_5,
8 | WMK_ANGLE_90_0,
9 | WMK_ANGLE_112_5,
10 | WMK_ANGLE_135_0,
11 | WMK_ANGLE_157_5,
12 | WMK_ANGLE_180_0,
13 | WMK_ANGLE_202_5,
14 | WMK_ANGLE_225_0,
15 | WMK_ANGLE_247_5,
16 | WMK_ANGLE_270_0,
17 | WMK_ANGLE_292_5,
18 | WMK_ANGLE_315_0,
19 | WMK_ANGLE_337_5,
20 | WMK_NUM_ANGLES
21 | };
22 |
23 | // Angle per index of wind vane (360 / 16 = 22.5)
24 | #define SFE_WIND_VANE_DEGREES_PER_INDEX (360.0 / WMK_NUM_ANGLES)
25 |
26 | // The ADC of each platform behaves slightly differently. Some have different
27 | // resolutions, some have non-linear outputs, and some voltage divider circuits
28 | // are different. The expected ADV values have been obtained experimentally for
29 | // various platforms below
30 | #ifdef AVR
31 | // Tested with RedBoard Qwiic with Weather Shield
32 | #define SFE_WMK_ADC_ANGLE_0_0 902
33 | #define SFE_WMK_ADC_ANGLE_22_5 661
34 | #define SFE_WMK_ADC_ANGLE_45_0 701
35 | #define SFE_WMK_ADC_ANGLE_67_5 389
36 | #define SFE_WMK_ADC_ANGLE_90_0 398
37 | #define SFE_WMK_ADC_ANGLE_112_5 371
38 | #define SFE_WMK_ADC_ANGLE_135_0 483
39 | #define SFE_WMK_ADC_ANGLE_157_5 430
40 | #define SFE_WMK_ADC_ANGLE_180_0 570
41 | #define SFE_WMK_ADC_ANGLE_202_5 535
42 | #define SFE_WMK_ADC_ANGLE_225_0 812
43 | #define SFE_WMK_ADC_ANGLE_247_5 792
44 | #define SFE_WMK_ADC_ANGLE_270_0 986
45 | #define SFE_WMK_ADC_ANGLE_292_5 925
46 | #define SFE_WMK_ADC_ANGLE_315_0 957
47 | #define SFE_WMK_ADC_ANGLE_337_5 855
48 |
49 | #define SFE_WMK_ADC_RESOLUTION 10
50 | #elif defined(ESP32)
51 | // Tested with ESP32 processor board installed on Weather Carrier
52 | #define SFE_WMK_ADC_ANGLE_0_0 3118
53 | #define SFE_WMK_ADC_ANGLE_22_5 1526
54 | #define SFE_WMK_ADC_ANGLE_45_0 1761
55 | #define SFE_WMK_ADC_ANGLE_67_5 199
56 | #define SFE_WMK_ADC_ANGLE_90_0 237
57 | #define SFE_WMK_ADC_ANGLE_112_5 123
58 | #define SFE_WMK_ADC_ANGLE_135_0 613
59 | #define SFE_WMK_ADC_ANGLE_157_5 371
60 | #define SFE_WMK_ADC_ANGLE_180_0 1040
61 | #define SFE_WMK_ADC_ANGLE_202_5 859
62 | #define SFE_WMK_ADC_ANGLE_225_0 2451
63 | #define SFE_WMK_ADC_ANGLE_247_5 2329
64 | #define SFE_WMK_ADC_ANGLE_270_0 3984
65 | #define SFE_WMK_ADC_ANGLE_292_5 3290
66 | #define SFE_WMK_ADC_ANGLE_315_0 3616
67 | #define SFE_WMK_ADC_ANGLE_337_5 2755
68 |
69 | #define SFE_WMK_ADC_RESOLUTION 12
70 | #else
71 | // Values calculated assuming 10k pullup and perfectly linear 16-bit ADC
72 | #define SFE_WMK_ADC_ANGLE_0_0 50294
73 | #define SFE_WMK_ADC_ANGLE_22_5 25985
74 | #define SFE_WMK_ADC_ANGLE_45_0 29527
75 | #define SFE_WMK_ADC_ANGLE_67_5 5361
76 | #define SFE_WMK_ADC_ANGLE_90_0 5958
77 | #define SFE_WMK_ADC_ANGLE_112_5 4219
78 | #define SFE_WMK_ADC_ANGLE_135_0 11818
79 | #define SFE_WMK_ADC_ANGLE_157_5 8099
80 | #define SFE_WMK_ADC_ANGLE_180_0 18388
81 | #define SFE_WMK_ADC_ANGLE_202_5 15661
82 | #define SFE_WMK_ADC_ANGLE_225_0 40329
83 | #define SFE_WMK_ADC_ANGLE_247_5 38365
84 | #define SFE_WMK_ADC_ANGLE_270_0 60494
85 | #define SFE_WMK_ADC_ANGLE_292_5 52961
86 | #define SFE_WMK_ADC_ANGLE_315_0 56785
87 | #define SFE_WMK_ADC_ANGLE_337_5 44978
88 |
89 | #define SFE_WMK_ADC_RESOLUTION 16
90 |
91 | // Set macro to indicate that this platform isn't known
92 | #define SFE_WMK_PLAFTORM_UNKNOWN
93 | #endif
--------------------------------------------------------------------------------