First commit
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
# How to Contribute
|
||||
|
||||
Thank you so *much* for offering to help out. We truly appreciate it.
|
||||
|
||||
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.
|
||||
Please check the [closed issues](https://github.com/sparkfun/SparkFun_Weather_Meter_Kit_Arduino_Library/issues?q=is%3Aissue+is%3Aclosed)
|
||||
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.
|
||||
|
||||
If you decide to add a feature to this library, please create a PR and follow these best practices:
|
||||
|
||||
* Change as little as possible. Do not submit a PR that changes 100 lines of whitespace. Break up into multiple PRs if necessary.
|
||||
* 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.
|
||||
* 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).
|
||||
* **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
|
||||
|
||||

|
||||
|
||||
## Style guide
|
||||
|
||||
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).
|
@@ -0,0 +1,43 @@
|
||||
SparkFun License Information
|
||||
============================
|
||||
|
||||
SparkFun uses two different licenses for our files — one for hardware and one for code.
|
||||
|
||||
Hardware
|
||||
---------
|
||||
|
||||
**SparkFun hardware is released under [Creative Commons Share-alike 4.0 International](http://creativecommons.org/licenses/by-sa/4.0/).**
|
||||
|
||||
Note: This is a human-readable summary of (and not a substitute for) the [license](http://creativecommons.org/licenses/by-sa/4.0/legalcode).
|
||||
|
||||
You are free to:
|
||||
|
||||
Share — copy and redistribute the material in any medium or format
|
||||
Adapt — remix, transform, and build upon the material
|
||||
for any purpose, even commercially.
|
||||
The licensor cannot revoke these freedoms as long as you follow the license terms.
|
||||
Under the following terms:
|
||||
|
||||
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.
|
||||
ShareAlike — If you remix, transform, or build upon the material, you must distribute your contributions under the same license as the original.
|
||||
No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.
|
||||
Notices:
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
|
||||
Code
|
||||
--------
|
||||
|
||||
**SparkFun code, firmware, and software are released under the [MIT License](http://opensource.org/licenses/MIT).**
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2020 SparkFun Electronics
|
||||
|
||||
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:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
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.
|
@@ -0,0 +1,91 @@
|
||||
SparkFun Weather Meter Kit Arduino Library
|
||||
========================================
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/sparkfun/SparkFun_Weather_Meter_Kit_Arduino_Library/issues" alt="Issues">
|
||||
<img src="https://img.shields.io/github/issues/sparkfun/SparkFun_Weather_Meter_Kit_Arduino_Library.svg" /></a>
|
||||
<a href="https://github.com/sparkfun/SparkFun_Weather_Meter_Kit_Arduino_Library/blob/master/LICENSE" alt="License">
|
||||
<img src="https://img.shields.io/badge/license-MIT-blue.svg" /></a>
|
||||
<a href="https://twitter.com/intent/follow?screen_name=sparkfun">
|
||||
<img src="https://img.shields.io/twitter/follow/sparkfun.svg?style=social&logo=twitter" alt="follow on Twitter"></a>
|
||||
<a href="https://github.com/sparkfun/SparkFun_Weather_Meter_Kit_Arduino_Library/actions" alt="Actions">
|
||||
<img src="https://github.com/sparkfun/SparkFun_Weather_Meter_Kit_Arduino_Library/actions/workflows/mkdocs.yml/badge.svg" /></a>
|
||||
</p>
|
||||
|
||||

|
||||
|
||||
[Weather Meter Kit](https://www.sparkfun.com/products/15901) *(SEN-15901)*
|
||||
|
||||
|
||||
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.
|
||||
|
||||
- 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.
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
|
||||
SparkFun labored with love to create this code. Feel like supporting open-source hardware and software? Buy a board from SparkFun!
|
||||
*This library is intended to be utilized with the [weather meter kit](https://www.sparkfun.com/products/15901) and the following boards:*
|
||||
|
||||
<table>
|
||||
<tr align="center">
|
||||
<td><a href="https://www.sparkfun.com/products/13956"><img src="https://cdn.sparkfun.com//assets/parts/1/1/6/6/5/13956-01.jpg" alt="Product Image - SparkFun Weather Shield"></a></td>
|
||||
<td><a href="https://www.sparkfun.com/products/16794"><img src="https://cdn.sparkfun.com//assets/parts/1/5/7/0/3/16794-SparkFun_MicroMod_Weather_Carrier_Board-01b.jpg" alt="Product Image - SparkFun MicroMod Weather Carrier Board"></a></td>
|
||||
</tr>
|
||||
<tr align="center">
|
||||
<td>SparkFun Weather Shield <i>[<a href="https://www.sparkfun.com/products/13956">DEV-13956</a>]</i></td>
|
||||
<td>SparkFun MicroMod Weather Carrier Board <i>[<a href="https://www.sparkfun.com/products/16794">SEN-16794</a>]</i></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Supported Microcontrollers - Arduino Environment
|
||||
|
||||
* Weather Shield
|
||||
* [ATMega328](https://www.sparkfun.com/products/18158)
|
||||
* MicroMod Weather Carrier Board
|
||||
* [Artemis](https://www.sparkfun.com/products/16401)
|
||||
* [SAMD51](https://www.sparkfun.com/products/16791)
|
||||
* [ESP32](https://www.sparkfun.com/products/16781)
|
||||
* [STM32](https://www.sparkfun.com/products/21326)
|
||||
* [nrf5280](https://www.sparkfun.com/products/16984)
|
||||
* [Teensy](https://www.sparkfun.com/products/16402)
|
||||
* [RP2040](https://www.sparkfun.com/products/17720)
|
||||
|
||||
Repository Contents
|
||||
-------------------
|
||||
|
||||
* [**/documents**](./documents) - Datasheet and User Manual
|
||||
* [**/examples**](./examples) - Example sketches for the library (.ino). Run these from the Arduino IDE.
|
||||
* [**/src**](./src) - Source files for the library (.cpp, .h).
|
||||
* [**keywords.txt**](./keywords.txt) - Keywords from this library that will be highlighted in the Arduino IDE.
|
||||
* [**library.properties**](./library.properties) - General library properties for the Arduino package manager.
|
||||
* [**CONTRIBUTING.md**](./CONTRIBUTING.md) - Guidelines on how to contribute to this library.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
* **[Installing an Arduino Library Guide](https://learn.sparkfun.com/tutorials/installing-an-arduino-library)** - Basic information on how to install an Arduino library
|
||||
* **[Assembly Guide](https://learn.sparkfun.com/tutorials/681)** - A tutorial for assembling the weather meter kit
|
||||
|
||||
|
||||
Products that use this Library
|
||||
------------------------------
|
||||
|
||||
* **[SEN-16794](https://www.sparkfun.com/products/16794)** - SparkFun MicroMod Weather Carrier Board
|
||||
* **[DEV-13956](https://www.sparkfun.com/products/13956)** - SparkFun Weather Shield
|
||||
* **[SEN-15901](https://www.sparkfun.com/products/15901)** - Weather Meter Kit
|
||||
|
||||
Contributing
|
||||
------------
|
||||
If you would like to contribute to this library: please do, we truly appreciate it, but please follow [these guidelines](./CONTRIBUTING.md). Thanks!
|
||||
|
||||
License Information
|
||||
-------------------
|
||||
|
||||
SparkFun's source files are ***open source***!
|
||||
|
||||
Please review the [`LICENSE.md`](LICENSE.md) file for license information.
|
||||
|
||||
Distributed as-is; no warranty is given.
|
||||
|
||||
\- Your friends at SparkFun.
|
Binary file not shown.
@@ -0,0 +1,60 @@
|
||||
#include "SparkFun_Weather_Meter_Kit_Arduino_Library.h"
|
||||
|
||||
// Below are the pin definitions for each sensor of the weather meter kit
|
||||
|
||||
// Pins for Weather Carrier with ESP32 Processor Board
|
||||
int windDirectionPin = 35;
|
||||
int windSpeedPin = 14;
|
||||
int rainfallPin = 27;
|
||||
|
||||
// Pins for the Weather Shield with SparkFun RedBoard Qwiic or Arduino Uno
|
||||
// int windDirectionPin = A0;
|
||||
// int windSpeedPin = 3;
|
||||
// int rainfallPin = 2;
|
||||
|
||||
// Create an instance of the weather meter kit
|
||||
SFEWeatherMeterKit weatherMeterKit(windDirectionPin, windSpeedPin, rainfallPin);
|
||||
|
||||
void setup()
|
||||
{
|
||||
// Begin serial
|
||||
Serial.begin(115200);
|
||||
Serial.println(F("SparkFun Weather Meter Kit Example 1 - Basic Readings"));
|
||||
Serial.println();
|
||||
Serial.println(F("Note - this example demonstrates the minimum code required"));
|
||||
Serial.println(F("for operation, and may not be accurate for your project."));
|
||||
Serial.println(F("It is recommended to check out the calibration examples."));
|
||||
|
||||
// Expected ADC values have been defined for various platforms in the
|
||||
// library, however your platform may not be included. This code will check
|
||||
// if that's the case
|
||||
#ifdef SFE_WMK_PLAFTORM_UNKNOWN
|
||||
// The platform you're using hasn't been added to the library, so the
|
||||
// expected ADC values have been calculated assuming a 10k pullup resistor
|
||||
// and a perfectly linear 16-bit ADC. Your ADC likely has a different
|
||||
// resolution, so you'll need to specify it here:
|
||||
weatherMeterKit.setADCResolutionBits(10);
|
||||
|
||||
Serial.println(F("Unknown platform! Please edit the code with your ADC resolution!"));
|
||||
Serial.println();
|
||||
#endif
|
||||
|
||||
// Begin weather meter kit
|
||||
weatherMeterKit.begin();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Print data from weather meter kit
|
||||
Serial.print(F("Wind direction (degrees): "));
|
||||
Serial.print(weatherMeterKit.getWindDirection(), 1);
|
||||
Serial.print(F("\t\t"));
|
||||
Serial.print(F("Wind speed (kph): "));
|
||||
Serial.print(weatherMeterKit.getWindSpeed(), 1);
|
||||
Serial.print(F("\t\t"));
|
||||
Serial.print(F("Total rainfall (mm): "));
|
||||
Serial.println(weatherMeterKit.getTotalRainfall(), 1);
|
||||
|
||||
// Only print once per second
|
||||
delay(1000);
|
||||
}
|
@@ -0,0 +1,102 @@
|
||||
#include "SparkFun_Weather_Meter_Kit_Arduino_Library.h"
|
||||
|
||||
// Below are the pin definitions for each sensor of the weather meter kit
|
||||
|
||||
// Pins for Weather Carrier with ESP32 Processor Board
|
||||
int windDirectionPin = 35;
|
||||
int windSpeedPin = 14;
|
||||
int rainfallPin = 27;
|
||||
|
||||
// Pins for the Weather Shield with SparkFun RedBoard Qwiic or Arduino Uno
|
||||
// int windDirectionPin = A0;
|
||||
// int windSpeedPin = 3;
|
||||
// int rainfallPin = 2;
|
||||
|
||||
// Create an instance of the weather meter kit
|
||||
SFEWeatherMeterKit weatherMeterKit(windDirectionPin, windSpeedPin, rainfallPin);
|
||||
|
||||
void setup()
|
||||
{
|
||||
// Begin serial
|
||||
Serial.begin(115200);
|
||||
Serial.println(F("SparkFun Weather Meter Kit Example 2 - Manual Calibration"));
|
||||
Serial.println();
|
||||
Serial.println(F("Note - this example demonstrates how to manually set the"));
|
||||
Serial.println(F("calibration parameters once you know what they are for your"));
|
||||
Serial.println(F("set up. If you don't know what values to use, check out"));
|
||||
Serial.println(F("Example 3, which walks you through it! The values used in"));
|
||||
Serial.println(F("this example are all defaults, so you may need to change them."));
|
||||
|
||||
// Here we create a struct to hold all the calibration parameters
|
||||
SFEWeatherMeterKitCalibrationParams calibrationParams = weatherMeterKit.getCalibrationParams();
|
||||
|
||||
// The wind vane has 8 switches, but 2 could close at the same time, which
|
||||
// results in 16 possible positions. Each position has a resistor connected
|
||||
// to GND, so this library assumes a voltage divider is created by adding
|
||||
// another resistor to VCC. Some of the wind vane resistor values are
|
||||
// fairly close to each other, meaning an accurate ADC is required. However
|
||||
// some ADCs have a non-linear behavior that causes this measurement to be
|
||||
// inaccurate. To account for this, the vane resistor values can be manually
|
||||
// changed here to compensate for the non-linear behavior of the ADC
|
||||
calibrationParams.vaneADCValues[WMK_ANGLE_0_0] = 3143;
|
||||
calibrationParams.vaneADCValues[WMK_ANGLE_22_5] = 1624;
|
||||
calibrationParams.vaneADCValues[WMK_ANGLE_45_0] = 1845;
|
||||
calibrationParams.vaneADCValues[WMK_ANGLE_67_5] = 335;
|
||||
calibrationParams.vaneADCValues[WMK_ANGLE_90_0] = 372;
|
||||
calibrationParams.vaneADCValues[WMK_ANGLE_112_5] = 264;
|
||||
calibrationParams.vaneADCValues[WMK_ANGLE_135_0] = 738;
|
||||
calibrationParams.vaneADCValues[WMK_ANGLE_157_5] = 506;
|
||||
calibrationParams.vaneADCValues[WMK_ANGLE_180_0] = 1149;
|
||||
calibrationParams.vaneADCValues[WMK_ANGLE_202_5] = 979;
|
||||
calibrationParams.vaneADCValues[WMK_ANGLE_225_0] = 2520;
|
||||
calibrationParams.vaneADCValues[WMK_ANGLE_247_5] = 2397;
|
||||
calibrationParams.vaneADCValues[WMK_ANGLE_270_0] = 3780;
|
||||
calibrationParams.vaneADCValues[WMK_ANGLE_292_5] = 3309;
|
||||
calibrationParams.vaneADCValues[WMK_ANGLE_315_0] = 3548;
|
||||
calibrationParams.vaneADCValues[WMK_ANGLE_337_5] = 2810;
|
||||
|
||||
// The rainfall detector contains a small cup that collects rain water. When
|
||||
// the cup fills, the water is dumped and the total rainfall is incremented
|
||||
// by some value. This value defaults to 0.2794mm of rain per count, as
|
||||
// specified by the datasheet
|
||||
calibrationParams.mmPerRainfallCount = 0.2794;
|
||||
|
||||
// The rainfall detector switch can sometimes bounce, causing multiple extra
|
||||
// triggers. This input is debounced by ignoring extra triggers within a
|
||||
// time window, which defaults to 100ms
|
||||
calibrationParams.minMillisPerRainfall = 100;
|
||||
|
||||
// The anemometer contains a switch that opens and closes as it spins. The
|
||||
// rate at which the switch closes depends on the wind speed. The datasheet
|
||||
// states that a wind of 2.4kph causes the switch to close once per second
|
||||
calibrationParams.kphPerCountPerSec = 2.4;
|
||||
|
||||
// Because the anemometer generates discrete pulses as it rotates, it's not
|
||||
// possible to measure the wind speed exactly at any point in time. A filter
|
||||
// is implemented in the library that averages the wind speed over a certain
|
||||
// time period, which defaults to 1 second. Longer intervals result in more
|
||||
// accurate measurements, but cause delay in the measurement
|
||||
calibrationParams.windSpeedMeasurementPeriodMillis = 1000;
|
||||
|
||||
// Now we can set all the calibration parameters at once
|
||||
weatherMeterKit.setCalibrationParams(calibrationParams);
|
||||
|
||||
// Begin weather meter kit
|
||||
weatherMeterKit.begin();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Print data from weather meter kit
|
||||
Serial.print(F("Wind direction (degrees): "));
|
||||
Serial.print(weatherMeterKit.getWindDirection(), 1);
|
||||
Serial.print(F("\t\t"));
|
||||
Serial.print(F("Wind speed (kph): "));
|
||||
Serial.print(weatherMeterKit.getWindSpeed(), 1);
|
||||
Serial.print(F("\t\t"));
|
||||
Serial.print(F("Total rainfall (mm): "));
|
||||
Serial.println(weatherMeterKit.getTotalRainfall(), 1);
|
||||
|
||||
// Only print once per second
|
||||
delay(1000);
|
||||
}
|
@@ -0,0 +1,309 @@
|
||||
#include "SparkFun_Weather_Meter_Kit_Arduino_Library.h"
|
||||
|
||||
// Below are the pin definitions for each sensor of the weather meter kit
|
||||
|
||||
// Pins for Weather Carrier with ESP32 Processor Board
|
||||
int windDirectionPin = 35;
|
||||
int windSpeedPin = 14;
|
||||
int rainfallPin = 27;
|
||||
|
||||
// Pins for the Weather Shield with SparkFun RedBoard Qwiic or Arduino Uno
|
||||
// int windDirectionPin = A0;
|
||||
// int windSpeedPin = 3;
|
||||
// int rainfallPin = 2;
|
||||
|
||||
// Create an instance of the weather meter kit
|
||||
SFEWeatherMeterKit weatherMeterKit(windDirectionPin, windSpeedPin, rainfallPin);
|
||||
|
||||
// Here we create a struct to hold all the calibration parameters
|
||||
SFEWeatherMeterKitCalibrationParams calibrationParams;
|
||||
|
||||
void setup()
|
||||
{
|
||||
// Begin serial
|
||||
Serial.begin(115200);
|
||||
Serial.println(F("SparkFun Weather Meter Kit Example 3 - Calibration Helper"));
|
||||
Serial.println();
|
||||
Serial.println(F("This example will help you determine the best calibration"));
|
||||
Serial.println(F("parameters to use for your project. Once each section is done,"));
|
||||
Serial.println(F("the values will be printed for you to copy into your sketch."));
|
||||
|
||||
// We'll be changing the calibration parameters one at a time, so we'll get
|
||||
// all the default values now
|
||||
calibrationParams = weatherMeterKit.getCalibrationParams();
|
||||
|
||||
// Begin weather meter kit
|
||||
weatherMeterKit.begin();
|
||||
|
||||
// Run the calibration helper
|
||||
runCalibrationHelper();
|
||||
|
||||
Serial.println();
|
||||
Serial.println(F("Calibration done! Enter any key to continue"));
|
||||
waitForUserInput();
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
// Print data from weather meter kit
|
||||
Serial.print(F("Wind direction (degrees): "));
|
||||
Serial.print(weatherMeterKit.getWindDirection(), 1);
|
||||
Serial.print(F("\t\t"));
|
||||
Serial.print(F("Wind speed (kph): "));
|
||||
Serial.print(weatherMeterKit.getWindSpeed(), 1);
|
||||
Serial.print(F("\t\t"));
|
||||
Serial.print(F("Total rainfall (mm): "));
|
||||
Serial.println(weatherMeterKit.getTotalRainfall(), 1);
|
||||
|
||||
// Only print once per second
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
void runCalibrationHelper()
|
||||
{
|
||||
// Run the helpers for each sensor
|
||||
runVaneCalibrationHelper();
|
||||
runRainfallCalibrationHelper();
|
||||
runAnemometerCalibrationHelper();
|
||||
}
|
||||
|
||||
void runVaneCalibrationHelper()
|
||||
{
|
||||
Serial.println();
|
||||
Serial.println(F("Wind vane calibration!"));
|
||||
|
||||
Serial.println();
|
||||
Serial.println(F("The wind vane has several switches, each with different"));
|
||||
Serial.println(F("resistors connected to GND. This library assumes there's an"));
|
||||
Serial.println(F("external resistor connected to VCC creating a voltage divider;"));
|
||||
Serial.println(F("the voltage is measured and compared with expected voltages"));
|
||||
Serial.println(F("for each direction. The expected voltages may need to be tuned,"));
|
||||
Serial.println(F("which this part walks you through. Hold the wind vane at the"));
|
||||
Serial.println(F("specified angle, then enter any key once steady. Pay close"));
|
||||
Serial.println(F("attention to the measured ADC value to see when it changes,"));
|
||||
Serial.println(F("especially around the 22.5 degree increments, they're very"));
|
||||
Serial.println(F("narrow! Enter any key to begin."));
|
||||
|
||||
// Wait for user to begin
|
||||
waitForUserInput();
|
||||
|
||||
// Loop through all angles
|
||||
for (int i = 0; i < WMK_NUM_ANGLES; i++)
|
||||
{
|
||||
// Compute current angle
|
||||
float currentAngle = i * SFE_WIND_VANE_DEGREES_PER_INDEX;
|
||||
|
||||
// Loop until user requests to continue
|
||||
clearUserInput();
|
||||
while (Serial.available() == 0)
|
||||
{
|
||||
Serial.print(F("Hold wind vane at "));
|
||||
Serial.print(currentAngle, 1);
|
||||
Serial.print(F(" degrees. Enter any key when in position."));
|
||||
Serial.print(F(" Measured ADC: "));
|
||||
Serial.print(analogRead(windDirectionPin));
|
||||
Serial.print(F(" Measured direction (degrees): "));
|
||||
Serial.println(weatherMeterKit.getWindDirection(), 1);
|
||||
|
||||
// Print moderately quickly so user can catch any brief changes
|
||||
delay(100);
|
||||
}
|
||||
|
||||
// Set this as the new expected ADC value for this angle
|
||||
uint32_t measuredADC = analogRead(windDirectionPin);
|
||||
calibrationParams.vaneADCValues[i] = measuredADC;
|
||||
weatherMeterKit.setCalibrationParams(calibrationParams);
|
||||
|
||||
// Print value for user to see
|
||||
Serial.println();
|
||||
Serial.print(F("Setting expected ADC value for "));
|
||||
Serial.print(currentAngle);
|
||||
Serial.print(F(" degrees to "));
|
||||
Serial.println(measuredADC);
|
||||
Serial.println();
|
||||
|
||||
// Wait a bit so user can read it
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
// Print the ADC value saved for each angle again so the user has it all in
|
||||
// one place
|
||||
Serial.println();
|
||||
Serial.println(F("Here are the ADC values set for each angle:"));
|
||||
Serial.println();
|
||||
for (int i = 0; i < WMK_NUM_ANGLES; i++)
|
||||
{
|
||||
// Compute current angle
|
||||
float currentAngle = i * SFE_WIND_VANE_DEGREES_PER_INDEX;
|
||||
|
||||
// Print this angle / ADC pair
|
||||
Serial.print(currentAngle, 1);
|
||||
Serial.print(F(" degrees: "));
|
||||
Serial.println(calibrationParams.vaneADCValues[i]);
|
||||
}
|
||||
|
||||
Serial.println();
|
||||
Serial.println(F("Wind vane calibration complete!"));
|
||||
}
|
||||
|
||||
void runRainfallCalibrationHelper()
|
||||
{
|
||||
Serial.println();
|
||||
Serial.println(F("Rainfall calibration!"));
|
||||
|
||||
// Rainfall calibration
|
||||
Serial.println();
|
||||
Serial.println(F("The rainfall detector contains a small cup that collects rain"));
|
||||
Serial.println(F("water. When the cup fills, the water gets dumped out and a"));
|
||||
Serial.println(F("counter is incremented. The exact volume of this cup needs to"));
|
||||
Serial.println(F("be known to get an accurate measurement of the total rainfall."));
|
||||
Serial.println(F("To calibrate this value, you'll need to pour a known volume"));
|
||||
Serial.println(F("of water into the rainfall detector, and the cup volume will"));
|
||||
Serial.println(F("be calculated. The rate at which the water is poured can"));
|
||||
Serial.println(F("affect the measurement, so go very slowly to simulate actual"));
|
||||
Serial.println(F("rain rather than dumping it all at once!"));
|
||||
Serial.println(F("Enter any key once you're ready to begin"));
|
||||
|
||||
// Wait for user to begin
|
||||
waitForUserInput();
|
||||
|
||||
// User is ready, reset the rainfall counter
|
||||
weatherMeterKit.resetTotalRainfall();
|
||||
|
||||
Serial.println();
|
||||
Serial.println(F("Begin pouring!"));
|
||||
Serial.println();
|
||||
|
||||
// Wait for user to finish
|
||||
clearUserInput();
|
||||
while (Serial.available() == 0)
|
||||
{
|
||||
Serial.print(F("Enter any key once finished pouring."));
|
||||
Serial.print(F(" Number of counts: "));
|
||||
Serial.print(weatherMeterKit.getRainfallCounts());
|
||||
Serial.print(F(" Measured rainfall (mm): "));
|
||||
Serial.println(weatherMeterKit.getTotalRainfall(), 1);
|
||||
|
||||
// Print slowly
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
Serial.println();
|
||||
Serial.println(F("Now enter the volume of water poured in mL"));
|
||||
waitForUserInput();
|
||||
int totalWaterML = Serial.parseInt();
|
||||
|
||||
// Convert ml to mm^3
|
||||
int totalWaterMM3 = totalWaterML * 1000;
|
||||
|
||||
// Divide by collection area of rainfall detector. It's about 50mm x 110mm,
|
||||
// resulting in a collection area of about 5500mm^2
|
||||
float totalRainfallMM = totalWaterMM3 / 5500.0;
|
||||
|
||||
// Divide by number of counts
|
||||
float mmPerCount = totalRainfallMM / weatherMeterKit.getRainfallCounts();
|
||||
|
||||
// Set this as the new mm per count
|
||||
calibrationParams.mmPerRainfallCount = mmPerCount;
|
||||
weatherMeterKit.setCalibrationParams(calibrationParams);
|
||||
|
||||
// Print value for user to see
|
||||
Serial.println();
|
||||
Serial.print(F("Setting mm per count to "));
|
||||
Serial.println(mmPerCount, 4);
|
||||
|
||||
Serial.println();
|
||||
Serial.println(F("Rainfall calibration complete!"));
|
||||
}
|
||||
|
||||
void runAnemometerCalibrationHelper()
|
||||
{
|
||||
Serial.println();
|
||||
Serial.println(F("Anemometer calibration!"));
|
||||
|
||||
Serial.println();
|
||||
Serial.println(F("This part will require you to place the anemometer in a"));
|
||||
Serial.println(F("constant wind stream for a few seconds, and you'll need to"));
|
||||
Serial.println(F("know or the wind speed or measure it with a calibrated"));
|
||||
Serial.println(F("anemometer (these can be purchased for relatively low cost)."));
|
||||
Serial.println(F("Enter the number of seconds you wish to run this calibration."));
|
||||
Serial.println(F("Longer will be more accurate, but the wind speed is more"));
|
||||
Serial.println(F("likely to fluctuate (10 seconds is recommended)"));
|
||||
waitForUserInput();
|
||||
int calibrationSeconds = Serial.parseInt();
|
||||
|
||||
// Set filter measurement period as requested
|
||||
calibrationParams.windSpeedMeasurementPeriodMillis = 1000 * calibrationSeconds;
|
||||
weatherMeterKit.setCalibrationParams(calibrationParams);
|
||||
|
||||
Serial.println();
|
||||
Serial.println(F("Now place the anemometer in a constant wind stream, and"));
|
||||
Serial.println(F("enter any key when ready to begin calibration"));
|
||||
waitForUserInput();
|
||||
|
||||
// Reset the wind speed filter to start the calibration period
|
||||
weatherMeterKit.resetWindSpeedFilter();
|
||||
|
||||
// Wait for calibration period to end
|
||||
Serial.println();
|
||||
for(int i = 0; i < calibrationSeconds; i++)
|
||||
{
|
||||
// Print time remaining
|
||||
Serial.print(F("Seconds remaining: "));
|
||||
Serial.println(calibrationSeconds - i);
|
||||
|
||||
// 1 second intervals
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
// Wait just a bit longer to make sure the filter window has passed
|
||||
delay(500);
|
||||
|
||||
// Store total number of wind speed counts
|
||||
uint32_t windCounts = weatherMeterKit.getWindSpeedCounts();
|
||||
|
||||
// Reset measurement period back to default
|
||||
calibrationParams.windSpeedMeasurementPeriodMillis = 1000;
|
||||
|
||||
Serial.println();
|
||||
Serial.println(F("Calibration period finished! Enter the average wind speed"));
|
||||
Serial.println(F("during the calibration period in kph"));
|
||||
waitForUserInput();
|
||||
float windSpeed = Serial.parseFloat();
|
||||
|
||||
// Calculate kph per count per second
|
||||
calibrationParams.kphPerCountPerSec = windSpeed * windCounts / calibrationSeconds;
|
||||
weatherMeterKit.setCalibrationParams(calibrationParams);
|
||||
|
||||
// Print value for user to see
|
||||
Serial.println();
|
||||
Serial.print(F("Setting kph per count per second to "));
|
||||
Serial.println(calibrationParams.kphPerCountPerSec, 2);
|
||||
|
||||
Serial.println();
|
||||
Serial.println(F("Anemometer calibration complete!"));
|
||||
}
|
||||
|
||||
void clearUserInput()
|
||||
{
|
||||
// Ensure all previous characters have come through
|
||||
delay(100);
|
||||
|
||||
// Throw away all previous characters
|
||||
while (Serial.available() != 0)
|
||||
{
|
||||
Serial.read();
|
||||
}
|
||||
}
|
||||
|
||||
void waitForUserInput()
|
||||
{
|
||||
// Remove previous user input
|
||||
clearUserInput();
|
||||
|
||||
// Wait for user to input something
|
||||
while (Serial.available() == 0)
|
||||
{
|
||||
// Nothing to do, keep waiting
|
||||
}
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
#########################################################
|
||||
# Syntax Coloring Map for SparkFun Weather Meter Kit #
|
||||
#########################################################
|
||||
# Class
|
||||
#########################################################
|
||||
|
||||
SFEWeatherMeterKit KEYWORD1
|
||||
|
||||
#########################################################
|
||||
# Methods and Functions
|
||||
#########################################################
|
||||
|
||||
begin KEYWORD2
|
||||
getWindDirection KEYWORD2
|
||||
getWindSpeed KEYWORD2
|
||||
getTotalRainfall KEYWORD2
|
||||
getCalibrationParams KEYWORD2
|
||||
setCalibrationParams KEYWORD2
|
||||
setADCResolutionBits KEYWORD2
|
||||
getWindSpeedCounts KEYWORD2
|
||||
getRainfallCounts KEYWORD2
|
||||
resetWindSpeedFilter KEYWORD2
|
||||
resetTotalRainfall KEYWORD2
|
||||
|
||||
#########################################################
|
||||
# Constants
|
||||
#########################################################
|
||||
|
||||
WMK_ANGLE_0_0 LITERAL1
|
||||
WMK_ANGLE_22_5 LITERAL1
|
||||
WMK_ANGLE_45_0 LITERAL1
|
||||
WMK_ANGLE_67_5 LITERAL1
|
||||
WMK_ANGLE_90_0 LITERAL1
|
||||
WMK_ANGLE_112_5 LITERAL1
|
||||
WMK_ANGLE_135_0 LITERAL1
|
||||
WMK_ANGLE_157_5 LITERAL1
|
||||
WMK_ANGLE_180_0 LITERAL1
|
||||
WMK_ANGLE_202_5 LITERAL1
|
||||
WMK_ANGLE_225_0 LITERAL1
|
||||
WMK_ANGLE_247_5 LITERAL1
|
||||
WMK_ANGLE_270_0 LITERAL1
|
||||
WMK_ANGLE_292_5 LITERAL1
|
||||
WMK_ANGLE_315_0 LITERAL1
|
||||
WMK_ANGLE_337_5 LITERAL1
|
||||
WMK_NUM_ANGLES LITERAL1
|
||||
SFE_WIND_VANE_DEGREES_PER_INDEX LITERAL1
|
||||
SFE_WIND_VANE_ADC_RESOLUTION_DEFAULT LITERAL1
|
||||
|
||||
#########################################################
|
||||
# Structs
|
||||
#########################################################
|
||||
|
||||
SFEWeatherMeterKitCalibrationParams LITERAL3
|
@@ -0,0 +1,9 @@
|
||||
name=SparkFun Weather Meter Kit Arduino Library
|
||||
version=1.1.1
|
||||
author=SparkFun Electronics
|
||||
maintainer=SparkFun Electronics <sparkfun.com>
|
||||
sentence=A library to use the SparkFun Weather Meter Kit
|
||||
paragraph=
|
||||
category=Sensors
|
||||
url=https://github.com/sparkfun/SparkFun_Weather_Meter_Kit_Arduino_Library
|
||||
architectures=*
|
@@ -0,0 +1,309 @@
|
||||
#include "SparkFun_Weather_Meter_Kit_Arduino_Library.h"
|
||||
|
||||
// Static member definitions
|
||||
SFEWeatherMeterKitCalibrationParams SFEWeatherMeterKit::_calibrationParams;
|
||||
uint32_t SFEWeatherMeterKit::_windCountsPrevious;
|
||||
uint32_t SFEWeatherMeterKit::_windCounts;
|
||||
uint32_t SFEWeatherMeterKit::_rainfallCounts;
|
||||
uint32_t SFEWeatherMeterKit::_lastWindSpeedMillis;
|
||||
uint32_t SFEWeatherMeterKit::_lastRainfallMillis;
|
||||
uint8_t SFEWeatherMeterKit::_windDirectionPin;
|
||||
uint8_t SFEWeatherMeterKit::_windSpeedPin;
|
||||
uint8_t SFEWeatherMeterKit::_rainfallPin;
|
||||
|
||||
/// @brief Default constructor, sets default calibration values
|
||||
SFEWeatherMeterKit::SFEWeatherMeterKit(uint8_t windDirectionPin, uint8_t windSpeedPin, uint8_t rainfallPin)
|
||||
{
|
||||
// Set sensors pins
|
||||
_windDirectionPin = windDirectionPin;
|
||||
_windSpeedPin = windSpeedPin;
|
||||
_rainfallPin = rainfallPin;
|
||||
|
||||
// The wind vane has 8 switches, but 2 could close at the same time, which
|
||||
// results in 16 possible positions. Each position has a different resistor,
|
||||
// resulting in different ADC values. The expected ADC values has been
|
||||
// experiemntally determined for various platforms, see the constants file
|
||||
_calibrationParams.vaneADCValues[WMK_ANGLE_0_0] = SFE_WMK_ADC_ANGLE_0_0;
|
||||
_calibrationParams.vaneADCValues[WMK_ANGLE_22_5] = SFE_WMK_ADC_ANGLE_22_5;
|
||||
_calibrationParams.vaneADCValues[WMK_ANGLE_45_0] = SFE_WMK_ADC_ANGLE_45_0;
|
||||
_calibrationParams.vaneADCValues[WMK_ANGLE_67_5] = SFE_WMK_ADC_ANGLE_67_5;
|
||||
_calibrationParams.vaneADCValues[WMK_ANGLE_90_0] = SFE_WMK_ADC_ANGLE_90_0;
|
||||
_calibrationParams.vaneADCValues[WMK_ANGLE_112_5] = SFE_WMK_ADC_ANGLE_112_5;
|
||||
_calibrationParams.vaneADCValues[WMK_ANGLE_135_0] = SFE_WMK_ADC_ANGLE_135_0;
|
||||
_calibrationParams.vaneADCValues[WMK_ANGLE_157_5] = SFE_WMK_ADC_ANGLE_157_5;
|
||||
_calibrationParams.vaneADCValues[WMK_ANGLE_180_0] = SFE_WMK_ADC_ANGLE_180_0;
|
||||
_calibrationParams.vaneADCValues[WMK_ANGLE_202_5] = SFE_WMK_ADC_ANGLE_202_5;
|
||||
_calibrationParams.vaneADCValues[WMK_ANGLE_225_0] = SFE_WMK_ADC_ANGLE_225_0;
|
||||
_calibrationParams.vaneADCValues[WMK_ANGLE_247_5] = SFE_WMK_ADC_ANGLE_247_5;
|
||||
_calibrationParams.vaneADCValues[WMK_ANGLE_270_0] = SFE_WMK_ADC_ANGLE_270_0;
|
||||
_calibrationParams.vaneADCValues[WMK_ANGLE_292_5] = SFE_WMK_ADC_ANGLE_292_5;
|
||||
_calibrationParams.vaneADCValues[WMK_ANGLE_315_0] = SFE_WMK_ADC_ANGLE_315_0;
|
||||
_calibrationParams.vaneADCValues[WMK_ANGLE_337_5] = SFE_WMK_ADC_ANGLE_337_5;
|
||||
|
||||
// Datasheet specifies 2.4kph of wind causes one trigger per second
|
||||
_calibrationParams.kphPerCountPerSec = 2.4;
|
||||
|
||||
// Wind speed sampling interval. Longer durations have more accuracy, but
|
||||
// cause delay and can miss fast fluctuations
|
||||
_calibrationParams.windSpeedMeasurementPeriodMillis = 1000;
|
||||
|
||||
// Datasheet specifies 0.2794mm of rain per trigger
|
||||
_calibrationParams.mmPerRainfallCount = 0.2794;
|
||||
|
||||
// Debounce time for rainfall detector
|
||||
_calibrationParams.minMillisPerRainfall = 100;
|
||||
|
||||
// Reset counters to zero
|
||||
_windCountsPrevious = 0;
|
||||
_windCounts = 0;
|
||||
_rainfallCounts = 0;
|
||||
|
||||
// Reset timers
|
||||
_lastWindSpeedMillis = millis();
|
||||
_lastRainfallMillis = millis();
|
||||
}
|
||||
|
||||
/// @brief Sets up sensor pins
|
||||
/// @param windDirectionPin Wind direction pin, must have an ADC
|
||||
/// @param windSpeedPin Wind speed pin, must support interrupts
|
||||
/// @param rainfallPin Rainfall pin, must support interrupts
|
||||
void SFEWeatherMeterKit::begin()
|
||||
{
|
||||
// Set pins to inputs
|
||||
pinMode(_windDirectionPin, INPUT);
|
||||
pinMode(_windSpeedPin, INPUT_PULLUP);
|
||||
pinMode(_rainfallPin, INPUT_PULLUP);
|
||||
|
||||
// Attach interrupt handlers
|
||||
attachInterrupt(digitalPinToInterrupt(_windSpeedPin), windSpeedInterrupt, CHANGE);
|
||||
attachInterrupt(digitalPinToInterrupt(_rainfallPin), rainfallInterrupt, RISING);
|
||||
}
|
||||
|
||||
/// @brief Gets the current calibration parameters
|
||||
/// @return Current calibration parameters
|
||||
SFEWeatherMeterKitCalibrationParams SFEWeatherMeterKit::getCalibrationParams()
|
||||
{
|
||||
return _calibrationParams;
|
||||
}
|
||||
|
||||
/// @brief Sets the new calibration parameters
|
||||
/// @param params New calibration parameters
|
||||
void SFEWeatherMeterKit::setCalibrationParams(SFEWeatherMeterKitCalibrationParams params)
|
||||
{
|
||||
// Copy the provided calibration parameters
|
||||
memcpy(&_calibrationParams, ¶ms, sizeof(SFEWeatherMeterKitCalibrationParams));
|
||||
}
|
||||
|
||||
/// @brief Adjusts the expected ADC values for the wind vane based on the
|
||||
/// provided ADC resolution
|
||||
/// @param resolutionBits Resolution of ADC in bits (eg. 8-bit, 12-bit, etc.)
|
||||
void SFEWeatherMeterKit::setADCResolutionBits(uint8_t resolutionBits)
|
||||
{
|
||||
for(uint8_t i = 0; i < WMK_NUM_ANGLES; i++)
|
||||
{
|
||||
int8_t bitShift = (SFE_WMK_ADC_RESOLUTION) - resolutionBits;
|
||||
|
||||
if(bitShift > 0)
|
||||
{
|
||||
_calibrationParams.vaneADCValues[i] >>= bitShift;
|
||||
}
|
||||
else if(bitShift < 0)
|
||||
{
|
||||
_calibrationParams.vaneADCValues[i] <<= -bitShift;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Measures the direction of the wind vane
|
||||
/// @return Wind direction in degrees
|
||||
float SFEWeatherMeterKit::getWindDirection()
|
||||
{
|
||||
// Measure the output of the voltage divider
|
||||
uint16_t rawADC = analogRead(_windDirectionPin);
|
||||
|
||||
// Now we'll loop through all possible directions to find which is closest
|
||||
// to our measurement, using a simple linear search. closestDifference is
|
||||
// initialized to max 16-bit signed value (2^15 - 1 = 32,767)
|
||||
int16_t closestDifference = 32767;
|
||||
uint8_t closestIndex = 0;
|
||||
for (uint8_t i = 0; i < WMK_NUM_ANGLES; i++)
|
||||
{
|
||||
// Compute the difference between the ADC value for this direction and
|
||||
// what we measured
|
||||
int16_t adcDifference = _calibrationParams.vaneADCValues[i] - rawADC;
|
||||
|
||||
// We only care about the magnitude of the difference
|
||||
adcDifference = abs(adcDifference);
|
||||
|
||||
// Check if this different is less than our closest so far
|
||||
if (adcDifference < closestDifference)
|
||||
{
|
||||
// This resistance is closer, update closest resistance and index
|
||||
closestDifference = adcDifference;
|
||||
closestIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
// Now compute the wind direction in degrees
|
||||
float direction = closestIndex * SFE_WIND_VANE_DEGREES_PER_INDEX;
|
||||
|
||||
// Return direction in degrees
|
||||
return direction;
|
||||
}
|
||||
|
||||
/// @brief Updates the wind speed measurement windows if needed
|
||||
void SFEWeatherMeterKit::updateWindSpeed()
|
||||
{
|
||||
// The anemometer generates interrupts as it spins. Because these are
|
||||
// discrete pulses, we can't get an instantaneous measurement of the wind
|
||||
// speed. Instead, we need to track these signals over time and perform some
|
||||
// filtering to get an estimate of the current wind speed. There's lots of
|
||||
// ways to do this, but this library uses a modifed version of a moving
|
||||
// window filter.
|
||||
//
|
||||
// A moving window filter would require an array of values to be stored,
|
||||
// indicating when each pulse occurred. However for a fixed time window, the
|
||||
// number of pulses is unknown, so we don't know how big the array needs to
|
||||
// be. There are some solutions to this, but the one used here is to change
|
||||
// the moving time window to a static time window, which is illustrated in
|
||||
// this timing diagram with variable time between pulses:
|
||||
//
|
||||
// Pulses | | | | | | | | |
|
||||
// Window Last window Current window
|
||||
// Time ------|-----------------------|----------------|
|
||||
// t_last t_now
|
||||
// |---Measurement Period--|---Measurement Period--|
|
||||
//
|
||||
// A counter is used to track the number of pulses detected in the current
|
||||
// measurement window; when pulses are detected, the counter is incremented.
|
||||
// When t_now exceeds the measurement period, the total number of pulses is
|
||||
// used to calculate the average wind speed for that window. This filter
|
||||
// only outputs wind speed for the previous window, which does result in
|
||||
// delayed measurements, but is fine for most data logging applications since
|
||||
// logs can be synced with the measurement widows
|
||||
|
||||
// Get current time
|
||||
uint32_t tNow = millis();
|
||||
|
||||
// Compute time since start of current measurement window
|
||||
uint32_t dt = tNow - _lastWindSpeedMillis;
|
||||
|
||||
// Check how long it's been since the start of this measurement window
|
||||
if (dt < _calibrationParams.windSpeedMeasurementPeriodMillis)
|
||||
{
|
||||
// Still within the current window, nothing to do (count is not
|
||||
// incremented here, that's done by the interrupt handler)
|
||||
}
|
||||
else
|
||||
{
|
||||
// We've passed the end of the measurement window, so we need to update
|
||||
// some things. But first, we need to check how long it's been since the
|
||||
// last time we updated, since it's possible we've not received any
|
||||
// pulses for a long time
|
||||
if (dt > (_calibrationParams.windSpeedMeasurementPeriodMillis * 2))
|
||||
{
|
||||
// Over 2 measurement periods have passed since the last update,
|
||||
// meaning the wind speed is very slow or even zero. So we'll reset
|
||||
// the wind speed and counter, and set the start of the next window
|
||||
// to be now
|
||||
_windCountsPrevious = 0;
|
||||
_windCounts = 0;
|
||||
_lastWindSpeedMillis = tNow;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We've only just gone past the end of the measurement period, so
|
||||
// save the wind counts for the previous window, reset current
|
||||
// counter, and update time of start of next measurement window
|
||||
_windCountsPrevious = _windCounts;
|
||||
_windCounts = 0;
|
||||
_lastWindSpeedMillis += _calibrationParams.windSpeedMeasurementPeriodMillis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// @brief Gets the measured wind speed
|
||||
/// @return Measured wind speed in kph
|
||||
float SFEWeatherMeterKit::getWindSpeed()
|
||||
{
|
||||
// Check if the wind speed needs to be updated
|
||||
updateWindSpeed();
|
||||
|
||||
// Calculate the wind speed for the previous window. First compute the
|
||||
// counts per millisecond
|
||||
float windSpeed = (float) _windCountsPrevious / _calibrationParams.windSpeedMeasurementPeriodMillis;
|
||||
|
||||
// Convert milliseconds to seconds, and counts per second to kph. Need to
|
||||
// divide by 2 to account for using both rising and falling edges
|
||||
windSpeed *= 1000 * _calibrationParams.kphPerCountPerSec / 2;
|
||||
|
||||
// Return wind speed for the previous measurement interval
|
||||
return windSpeed;
|
||||
}
|
||||
|
||||
/// @brief Gets the number of wind speed counts
|
||||
/// @return Number of wind speed counts
|
||||
uint32_t SFEWeatherMeterKit::getWindSpeedCounts()
|
||||
{
|
||||
// Return total wind speed counts
|
||||
return _windCounts;
|
||||
}
|
||||
|
||||
/// @brief Gets the number of rainfall counts
|
||||
/// @return Number of rainfall counts
|
||||
uint32_t SFEWeatherMeterKit::getRainfallCounts()
|
||||
{
|
||||
// Return total rainfall counts
|
||||
return _rainfallCounts;
|
||||
}
|
||||
|
||||
/// @brief Gets the total rainfall
|
||||
/// @return Total rainfall in mm
|
||||
float SFEWeatherMeterKit::getTotalRainfall()
|
||||
{
|
||||
// Return total rainfall in mm
|
||||
return _rainfallCounts * _calibrationParams.mmPerRainfallCount;
|
||||
}
|
||||
|
||||
/// @brief Resets the wind speed
|
||||
void SFEWeatherMeterKit::resetWindSpeedFilter()
|
||||
{
|
||||
_windCountsPrevious = 0;
|
||||
_windCounts = 0;
|
||||
_lastWindSpeedMillis = millis();
|
||||
}
|
||||
|
||||
/// @brief Resets the total rainfall
|
||||
void SFEWeatherMeterKit::resetTotalRainfall()
|
||||
{
|
||||
_rainfallCounts = 0;
|
||||
}
|
||||
|
||||
/// @brief Interrupt handler for wind speed pin
|
||||
void SFEWeatherMeterKit::windSpeedInterrupt()
|
||||
{
|
||||
// Check if the measurement window needs to be updated
|
||||
updateWindSpeed();
|
||||
|
||||
// Increment counts in this measurement window
|
||||
_windCounts++;
|
||||
}
|
||||
|
||||
/// @brief Interrupt handler for rainfall pin
|
||||
void SFEWeatherMeterKit::rainfallInterrupt()
|
||||
{
|
||||
// Debounce by checking time since last interrupt
|
||||
if ((millis() - _lastRainfallMillis) < _calibrationParams.minMillisPerRainfall)
|
||||
{
|
||||
// There's not been enough time since the last interrupt, so this is
|
||||
// likely just the switch bouncing
|
||||
return;
|
||||
}
|
||||
|
||||
// Enough time has passed that this is probably a real signal instead of a
|
||||
// bounce, so update the time of the last interrupt to be now
|
||||
_lastRainfallMillis = millis();
|
||||
|
||||
// Increment counter
|
||||
_rainfallCounts++;
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
#ifndef __SPARKFUN_WEATHER_METER_KIT_H__
|
||||
#define __SPARKFUN_WEATHER_METER_KIT_H__
|
||||
|
||||
#include "Arduino.h"
|
||||
#include "SparkFun_Weather_Meter_Kit_Constants.h"
|
||||
|
||||
// Calibration parameters for each sensor
|
||||
struct SFEWeatherMeterKitCalibrationParams
|
||||
{
|
||||
// Wind vane
|
||||
uint16_t vaneADCValues[WMK_NUM_ANGLES];
|
||||
|
||||
// Wind speed
|
||||
uint32_t windSpeedMeasurementPeriodMillis;
|
||||
float kphPerCountPerSec;
|
||||
|
||||
// Rainfall
|
||||
float mmPerRainfallCount;
|
||||
uint32_t minMillisPerRainfall;
|
||||
};
|
||||
|
||||
class SFEWeatherMeterKit
|
||||
{
|
||||
public:
|
||||
// Constructor
|
||||
SFEWeatherMeterKit(uint8_t windDirectionPin, uint8_t windSpeedPin, uint8_t rainfallPin);
|
||||
static void begin();
|
||||
|
||||
// Data collection
|
||||
static float getWindDirection();
|
||||
static float getWindSpeed();
|
||||
static float getTotalRainfall();
|
||||
|
||||
// Sensor calibration params
|
||||
static SFEWeatherMeterKitCalibrationParams getCalibrationParams();
|
||||
static void setCalibrationParams(SFEWeatherMeterKitCalibrationParams params);
|
||||
|
||||
// ADC resolution scaling
|
||||
static void setADCResolutionBits(uint8_t resolutionBits);
|
||||
|
||||
// Helper functions. These can be helpful for sensor calibration
|
||||
static uint32_t getWindSpeedCounts();
|
||||
static uint32_t getRainfallCounts();
|
||||
static void resetWindSpeedFilter();
|
||||
static void resetTotalRainfall();
|
||||
|
||||
private:
|
||||
// Updates wind speed
|
||||
static void updateWindSpeed();
|
||||
|
||||
// Interrupt handlers
|
||||
static void windSpeedInterrupt();
|
||||
static void rainfallInterrupt();
|
||||
|
||||
// Pins for each sensor
|
||||
static uint8_t _windDirectionPin;
|
||||
static uint8_t _windSpeedPin;
|
||||
static uint8_t _rainfallPin;
|
||||
|
||||
// Sensor calibration parameters
|
||||
static SFEWeatherMeterKitCalibrationParams _calibrationParams;
|
||||
|
||||
// Variables to track measurements
|
||||
static uint32_t _windCounts;
|
||||
static uint32_t _windCountsPrevious;
|
||||
static uint32_t _rainfallCounts;
|
||||
static uint32_t _lastWindSpeedMillis;
|
||||
static uint32_t _lastRainfallMillis;
|
||||
};
|
||||
|
||||
#endif
|
@@ -0,0 +1,93 @@
|
||||
// Enum to define the indexes for each wind direction
|
||||
enum SFEWeatherMeterKitAnemometerAngles
|
||||
{
|
||||
WMK_ANGLE_0_0 = 0,
|
||||
WMK_ANGLE_22_5,
|
||||
WMK_ANGLE_45_0,
|
||||
WMK_ANGLE_67_5,
|
||||
WMK_ANGLE_90_0,
|
||||
WMK_ANGLE_112_5,
|
||||
WMK_ANGLE_135_0,
|
||||
WMK_ANGLE_157_5,
|
||||
WMK_ANGLE_180_0,
|
||||
WMK_ANGLE_202_5,
|
||||
WMK_ANGLE_225_0,
|
||||
WMK_ANGLE_247_5,
|
||||
WMK_ANGLE_270_0,
|
||||
WMK_ANGLE_292_5,
|
||||
WMK_ANGLE_315_0,
|
||||
WMK_ANGLE_337_5,
|
||||
WMK_NUM_ANGLES
|
||||
};
|
||||
|
||||
// Angle per index of wind vane (360 / 16 = 22.5)
|
||||
#define SFE_WIND_VANE_DEGREES_PER_INDEX (360.0 / WMK_NUM_ANGLES)
|
||||
|
||||
// The ADC of each platform behaves slightly differently. Some have different
|
||||
// resolutions, some have non-linear outputs, and some voltage divider circuits
|
||||
// are different. The expected ADV values have been obtained experimentally for
|
||||
// various platforms below
|
||||
#ifdef AVR
|
||||
// Tested with RedBoard Qwiic with Weather Shield
|
||||
#define SFE_WMK_ADC_ANGLE_0_0 902
|
||||
#define SFE_WMK_ADC_ANGLE_22_5 661
|
||||
#define SFE_WMK_ADC_ANGLE_45_0 701
|
||||
#define SFE_WMK_ADC_ANGLE_67_5 389
|
||||
#define SFE_WMK_ADC_ANGLE_90_0 398
|
||||
#define SFE_WMK_ADC_ANGLE_112_5 371
|
||||
#define SFE_WMK_ADC_ANGLE_135_0 483
|
||||
#define SFE_WMK_ADC_ANGLE_157_5 430
|
||||
#define SFE_WMK_ADC_ANGLE_180_0 570
|
||||
#define SFE_WMK_ADC_ANGLE_202_5 535
|
||||
#define SFE_WMK_ADC_ANGLE_225_0 812
|
||||
#define SFE_WMK_ADC_ANGLE_247_5 792
|
||||
#define SFE_WMK_ADC_ANGLE_270_0 986
|
||||
#define SFE_WMK_ADC_ANGLE_292_5 925
|
||||
#define SFE_WMK_ADC_ANGLE_315_0 957
|
||||
#define SFE_WMK_ADC_ANGLE_337_5 855
|
||||
|
||||
#define SFE_WMK_ADC_RESOLUTION 10
|
||||
#elif ESP32
|
||||
// Tested with ESP32 processor board installed on Weather Carrier
|
||||
#define SFE_WMK_ADC_ANGLE_0_0 3118
|
||||
#define SFE_WMK_ADC_ANGLE_22_5 1526
|
||||
#define SFE_WMK_ADC_ANGLE_45_0 1761
|
||||
#define SFE_WMK_ADC_ANGLE_67_5 199
|
||||
#define SFE_WMK_ADC_ANGLE_90_0 237
|
||||
#define SFE_WMK_ADC_ANGLE_112_5 123
|
||||
#define SFE_WMK_ADC_ANGLE_135_0 613
|
||||
#define SFE_WMK_ADC_ANGLE_157_5 371
|
||||
#define SFE_WMK_ADC_ANGLE_180_0 1040
|
||||
#define SFE_WMK_ADC_ANGLE_202_5 859
|
||||
#define SFE_WMK_ADC_ANGLE_225_0 2451
|
||||
#define SFE_WMK_ADC_ANGLE_247_5 2329
|
||||
#define SFE_WMK_ADC_ANGLE_270_0 3984
|
||||
#define SFE_WMK_ADC_ANGLE_292_5 3290
|
||||
#define SFE_WMK_ADC_ANGLE_315_0 3616
|
||||
#define SFE_WMK_ADC_ANGLE_337_5 2755
|
||||
|
||||
#define SFE_WMK_ADC_RESOLUTION 12
|
||||
#else
|
||||
// Values calculated assuming 10k pullup and perfectly linear 16-bit ADC
|
||||
#define SFE_WMK_ADC_ANGLE_0_0 50294
|
||||
#define SFE_WMK_ADC_ANGLE_22_5 25985
|
||||
#define SFE_WMK_ADC_ANGLE_45_0 29527
|
||||
#define SFE_WMK_ADC_ANGLE_67_5 5361
|
||||
#define SFE_WMK_ADC_ANGLE_90_0 5958
|
||||
#define SFE_WMK_ADC_ANGLE_112_5 4219
|
||||
#define SFE_WMK_ADC_ANGLE_135_0 11818
|
||||
#define SFE_WMK_ADC_ANGLE_157_5 8099
|
||||
#define SFE_WMK_ADC_ANGLE_180_0 18388
|
||||
#define SFE_WMK_ADC_ANGLE_202_5 15661
|
||||
#define SFE_WMK_ADC_ANGLE_225_0 40329
|
||||
#define SFE_WMK_ADC_ANGLE_247_5 38365
|
||||
#define SFE_WMK_ADC_ANGLE_270_0 60494
|
||||
#define SFE_WMK_ADC_ANGLE_292_5 52961
|
||||
#define SFE_WMK_ADC_ANGLE_315_0 56785
|
||||
#define SFE_WMK_ADC_ANGLE_337_5 44978
|
||||
|
||||
#define SFE_WMK_ADC_RESOLUTION 16
|
||||
|
||||
// Set macro to indicate that this platform isn't known
|
||||
#define SFE_WMK_PLAFTORM_UNKNOWN
|
||||
#endif
|
Reference in New Issue
Block a user