Welcome to the next tutorial in our Raspberry Pi 4 programming tutorial. In the previous tutorial, we learned the basics of using a Raspberry Pi as the basis for an Internet of Things-based weather station. However, this is a continuation of the previous tutorial. In the subsequent session, we will learn how to develop the Python code that will bring our weather station circuit design to life. Before that, let us learn how to measure wind direction and rainfall.

## Wind direction

### Wind vanes

Despite their name, wind vanes do not indicate a change in wind direction. Since the arrow on most television weather maps goes in the opposite direction, this may come as a surprise at first. To determine the wind's direction, a wind vane uses the force of the wind on a vertical blade, which then spins to the point of least wind resistance.

### How your wind vane works

The wind vane employed here is more sophisticated than the rain gauge and operates entirely differently, although sharing some of the same components. You may find eight reed switches, resembling the spokes of a wheel, within the recommended wind vane.

In addition to the magnet, the wind vane has eight resistors, and each is turned on and off by the opening and closing of a matching reed switch as the magnet rotates.

### The role of the resistors

In electronics, these components slow down the passage of electricity without completely blocking it. Different resistors have varying resistance levels, measured in ohms; low resistance resistors allow nearly all current to pass through, whereas high resistance resistors allow very little current to pass through. Resistors are commonly used to prevent harmful currents from reaching components or distributing circuit power.

You should be able to read the values of the eight resistors displayed in white next to each one. Because the magnet may close two adjacent reed switches when positioned halfway between them, the wind vane can have 16 distinct resistance settings. Since most Pi-compatible wind vanes function in a similar fashion, you can find the resistor values in the product info sheet of your specific model.

### Measuring the resistance

To determine the wind's direction, a sensor's resistance must be measured and converted to an angle's value. This procedure involves a number of stages. It is much simpler to record a value from the wind vane that changes depending on the resistance value used. Thus, you will make an analog measurement, as the wind vane constantly returns a dynamic voltage reading. Conversely, the anemometer merely reports a 'HIGH' or 'LOW' voltage, all or nothing, thus sending a digital signal.

### Measuring analog voltages with a Raspberry Pi

Raspberry Pi has just digital inputs, but an Arduino has analog ones. Thus, a specialized component known as an analog-to-digital converter is required to decode an analog signal (ADC).

The MCP3008 is a widely used and flexible ADC. It's a 16-pin IC with eight input signals and can be utilized with a breadboard without much hassle. In other words, the MCP3008 can detect voltage changes as small as 4.88mV for a 5V reference voltage because it is a 10-bit ADC with 210 = 1024 possible output values.

Now that you know how to utilize the MCP3008 to measure a fluctuating analog signal, you can employ yet another ingenious circuit to generate a value that varies with the wind vane's resistance.

### Using a voltage divider

One of the essential electronic circuits is a voltage divider, which splits a significant voltage into smaller ones.

You can use the following formula to determine the voltage output Vout in the circuit above:

Vout = Vin * R2/(R1 + R2)

So, you can lower the input value Vin to the voltage output Vout by adjusting the value of R1 and R2. Create an entirely new Python program with the name voltage-divider.py that has the function voltage divider that determines Vout for a set of specified values for R1, R2, and Vin using this formula.

Verify that your function returns the correct value from a given set of inputs. The function should provide an answer of 3.837V, for instance, when R1 = 33K ohms, R2 = 10K ohms, and Vin = 5V.

print(voltage_divider(33000,10000,5.0))

According to the circuit, if R2 is a variable resistor, we may determine its resistance by monitoring its output voltage, Vout, and the resistance of its counterpart, R1. Due to the wind vane's behavior as a variable resistor, its resistance value may be determined using a voltage divider circuit. Initially, it would help if you determined the optimal value for R1.

### Designing a voltage divider

A voltage-divider circuit schematic and a table detailing angles, resistances, and voltages can be found on the second wind vane data sheet. R1 is depicted here with a value of 10K ohms. Vin, however, is referenced to 5V in this circuit. Although the Raspberry Pi uses 3.3V logic levels, these Vout values are off.

Create a small Python program named vane values.py that uses the datasheet's list of resistances and the potential divider formula to determine the updated values for a 3.3V Vin and an R1 resistant of 10K ohms.

A reference voltage of 5V allows a value of R1 = 10K ohms to be used successfully. However, with 3.3V, you'll see that some of the potential values are very close. Setting R1 to a lower value can minimize the voltage jumps between vane-generated resistance levels.

To try out different options for R1, use your vane values.py program. Keep in mind that there are only a small number of predefined resistance settings to choose from. The most typical values (in tens of thousands of ohms):

It would be best to use a wind vane with an ohms value of 4.7K. You may now connect your ADC and Raspberry Pi to the rest of the circuitry, as you have the value for Resistor1 in the voltage-divider circuit.

The gpiozero package makes it simple to read data from an MCP3008 ADC.

from gpiozero import MCP3008

import time

This code samples the ADC's channel 0 and outputs the value scaled between Zero and one. The recorded analog voltage can be calculated by multiplying the result by the reference voltage fed into the ADC.

Ensure your circuit can tell the difference between the wind vane's various angular locations by testing it. Make a simple Python script in the directory /home/pi/weather-station/wind direction byo.py to tally the data your circuit outputs when the vane is turned.

While the wind vane is turning, your code should be executed. Using the Python shell, you should see the total number of different voltages encountered thus far shown.

Red warning highlighting for 'SPISoftwareFallback' may also appear. You can safely disregard this warning, but if you never want to see it again, navigate to Raspberry > Pi Configuration in the Raspberry menu. Afterward, you should go to the Interfaces tab and enable SPI before rebooting your Pi.

At most, you can record 16 distinct voltages if your equipment is functioning well. Still, the Analogue to digital converter may record an increasing or decreasing voltage, so a moderate jiggling of the vane may allow you to generate a few additional values.

Update your program to verify that each ADC reading is compared to a predetermined set of valid values. If your code is able to do so, it should output a helpful message after each reading indicating whether or not the value was within the acceptable range.

The final process involves transforming the vane's measurements into angles. The angle-resistance-voltage relationship is fundamental. A wind vane's resistance design reflects the blade angle in direct proportion to the voltage value read from the ADC.

The resistance-voltage relationship can be determined with the help of the voltage divider function you programmed before. After you know the angle, you can find the equivalent value in the manual. An ADC reading of 0.4V, for instance, translates to a resistance of 3.3K ohms, representing a zero-degree angle.

Make the necessary edits to the wind direction byo.py file to convert the voltages in the list into a Python dictionary, where the voltages will serve as the keys and the related angles will serve as the values.

It would help to modify your print statements to show the angle of the vane. You can now measure wind direction using your very own Python program. You may verify the code is functioning properly by adjusting the wind vane to a known position and seeing if it matches what is displayed. To try out other roles, keep repeating the process.

Taking many readings in a short amount of time and averaging them together is a final way to increase the reliability of your data. Include the following procedure in wind-direction-by.py.

def get_average(angles):

sin_sum = 0.0

cos_sum = 0.0

for angle in angles:

sin_sum += math.sin(r)

cos_sum += math.cos(r)

flen = float(len(angles))

s = sin_sum / flen

c = cos_sum / flen

arc = math.degrees(math.atan(s / c))

average = 0.0

if s > 0 and c > 0:

average = arc

elif c < 0:

average = arc + 180

elif s < 0 and c > 0:

average = arc + 360

return 0.0 if average == 360 else average

A line like this at the beginning of your file will import the math library, allowing you to use the package.

import math

Now, similar to how you tested for wind gusts before, you should edit your program to include a function called get value() that provides the overall average for a specified period. This will facilitate calling this function from anywhere in the code for your weather station.

## Rainfall

The standard unit of measurement for precipitation captured by most rain gauges is the number of millimeters of height over a particular area of one square meter.

An essential mechanical instrument is the rain gauge sensor suggested for use with the Pi 4 Weather Station equipment.

### How does it work?

Remove the bucket to inspect the inner workings of the rain gauge. To remove the lid, gently squeeze the clamps on either side.

Put this rain gauge functions as a self-tipping bucket. All the rain runs off and into the bucket. When the bucket is complete, it will topple over, allowing the gathered rainwater to drain out the bottom, and the other bucket to rise into its place.

The total rainfall can be determined by multiplying this by the total number of tips. To find out how much water is needed if you're using a rain gauge of a different kind, you can either look it up in the manual or try it out for yourself.

These gauges typically have an RJ11 socket, despite only having a single red and green wire. You should be able to locate a small cylindrical magnet inside the ridge between the two buckets, with the magnet's axis pointing toward the back wall. There's a reed switch hidden inside that wall in the back.

If you want to peek inside, the back wall may be removed by pulling on the flat end. A removable circuit board allows you to inspect the inside components. You'll find the reed switch smack dab in the center. Make sure to swap out the circuit board and the cover for the back wall before proceeding.

When a bucket is knocked over, the magnet will move beyond the reed switch and temporarily close it. Hence, like the anemometer, the rain gauge can be connected to gpio pins on the Pi 4 and used as a button, with the number of "presses" being used as a proxy for the amount of precipitation.

The rain gauge can be tested by removing the RJ11 connector, stripping the wires, or using an RJ11 breakout board.

To avoid inconsistencies, it's recommended that you connect your device to the same GPIO pin 6 (BCM) that the Oracle Weather Station uses for its rain gauge.

Create a program named /home/pi/weather-station/rainfall.py to determine when the rain gauge bucket has tipped using the same logic you implemented for the anemometer. It should provide a running total of times the bucket has been tipped.

Once you've mastered counting raindrops in a bucket, you'll need to figure out how many feet of water this translates to. Change your program so that when the bucket is tipped, the amount of rain in inches is displayed.

Last, implement the function reset rainfall to reset the number of tipped buckets to 0. After this step, your weather station will only be fully functional with this feature.

## Fully functional weather station

After ensuring that each sensor works independently, you may move on to configuring the software for your data-gathering system.

Thus far, your GPIO connections have been consistent with what is required by the original Oracle pi 4 Weather Station software, which is executed using a Unix daemon. Hence, with a few tweaks, you can use that code to execute your custom build. In reality, the DS18B20 digital thermometer you're using was initially programmed for the Oracle Weather Station.

You may use the code you've built for testing to add the finishing touches to your weather station by regularly measuring and recording data.

## Wind speed, gusts, and direction

The core of your weather station app will be the code you created to measure wind speed and gusts. You should create a new script off of wind.py and name it weather station BYO.py.

Besides the applications you've previously built, you'll also have to incorporate various libraries and the code from Oracle's Weather Station. These import statements should be placed at the very beginning of your script:

from gpiozero import Button

import time

import math

import bme280_sensor

import wind_direction_byo

import statistics

import ds18b20_therm

As written, your code will keep a running tally of wind speeds every five seconds, highlighting the highest reading (gusts) and averaging them to determine an average. You can modify it to measure the wind's direction in real-time.

Rather than waiting for five seconds between iterations, change your code to measure the wind's direction for five seconds continuously. Do a code check by turning the wind vane and the anemometer to see how they respond to your program. Do logical results emerge from the code?

As fresh data comes in, your application automatically adjusts the averages and maximum wind speed. After every five seconds, your device should begin taking new readings. Insert two lines of code to clear the wind speed and direction arrays after each loop iteration.

Try rerunning the tests. Throughout the next five seconds, you'll want to keep track of the wind vane's angular position by counting the anemometer's rotations. Your program then determines the average wind speed and vane position throughout that time frame. By clearing the list of velocities after each five-second interval, you should also have noticed that the reading for wind gusts is now identical to the mean (the last one).

If you want to save new readings, a sampling interval of five seconds is too short. If you want to keep track of things indefinitely, a 5-minute measurement interval is ideal. In order to capture the highest value (gust), it is possible to take readings for five minutes and then average them. That is far more practical.

Change the program to take readings once every five seconds instead of once every minute. Then, utilize those values to determine the average wind speed and direction and log the most unexpected gust once every five minutes.

Do some tests on your code. Now every five minutes, it should report its status. By turning the vane and anemometer, you may create a wind tunnel and ensure your readings are consistent with expectations. The other sensors can be incorporated into the five-minute cycle now.

## Rainfall

The rain measurement code you wrote in rainfall.py should be incorporated into weather station BYO.py to record the total for five minutes and then reset. After the import statements, add the constant definition for the bucket size.

BUCKET_SIZE = 0.2794

Add these lines before the while True: loop.

def bucket_tipped():

global rain_count

rain_count = rain_count + 1

#print (rain_count * BUCKET_SIZE)

def reset_rainfall():

global rain_count

rain_count = 0

rain_sensor = Button(6)

rain_sensor.when_pressed = bucket_tipped

Then, insert the following code after the ones that determine wind speed and gusts:

rainfall = rain_count * BUCKET_SIZE

reset_rainfall()

## Temperature, pressure, and humidity

You implemented a read-all function in the BME280 sensor's code to return all three readings—pressure, temperature, and humidity. Because you have imported the bme280 sensor into weather station BYO.py, you may now use this feature whenever needed.

Adjust your program so that the BME280 sensor's data is saved at regular five-minute intervals.

Please make sure your code works. Make sure the BME280's readings change as you exhale into it.

## Ground temperature

After that, repeat the process using the ground temp probe. Make changes to your code to gather data at 5-minute intervals.

## Complete code

from gpiozero import Button

import time

import math

import bme280_sensor

import wind_direction_byo

import statistics

import ds18b20_therm

import database

wind_count = 0       # Counts how many half-rotations

wind_interval = 5 # How often (secs) to sample speed

interval =  5 # measurements recorded every 5 minutes

CM_IN_A_KM = 100000.0

SECS_IN_AN_HOUR = 3600

BUCKET_SIZE = 0.2794

rain_count = 0

gust = 0

store_speeds = []

store_directions = []

# Every half-rotation, add 1 to count

def spin():

global wind_count

wind_count = wind_count + 1

#print( wind_count )

def calculate_speed(time_sec):

global wind_count

global gust

circumference_cm = (2 * math.pi) * radius_cm

rotations = wind_count / 2.0

# Calculate distance travelled by a cup in km

dist_km = (circumference_cm * rotations) / CM_IN_A_KM

# Speed = distance / time

km_per_sec = dist_km / time_sec

km_per_hour = km_per_sec * SECS_IN_AN_HOUR

# Calculate speed

return final_speed

def bucket_tipped():

global rain_count

rain_count = rain_count + 1

#print (rain_count * BUCKET_SIZE)

def reset_rainfall():

global rain_count

rain_count = 0

def reset_wind():

global wind_count

wind_count = 0

def reset_gust():

global gust

gust = 0

wind_speed_sensor = Button(5)

wind_speed_sensor.when_activated = spin

temp_probe = ds18b20_therm.DS18B20()

while True:

start_time = time.time()

while time.time() - start_time <= interval:

wind_start_time = time.time()

reset_wind()

#time.sleep(wind_interval)

while time.time() - wind_start_time <= wind_interval:

store_directions.append(wind_direction_byo.get_value())

final_speed = calculate_speed(wind_interval)# Add this speed to the list

store_speeds.append(final_speed)

wind_average = wind_direction_byo.get_average(store_directions)

wind_gust = max(store_speeds)

wind_speed = statistics.mean(store_speeds)

rainfall = rain_count * BUCKET_SIZE

reset_rainfall()

store_speeds = []

#print(store_directions)

store_directions = []

print(wind_average, wind_speed, wind_gust, rainfall,  humidity, pressure, ambient_temp, ground_temp)

## Keeping your weather station dry

I can't stress the significance of this enough. The Pi and the other electronics will break down or corrode if they get wet. A small weatherproof shell protects the external environment sensors in the Oracle Pi 4 Weather Station. The central concept is to let fresh air flow past the sensors while keeping moisture away.

### Weatherproof boxes

Track down two watertight containers, one big enough to house the Pi and breadboard/HAT and another smaller one for the BME280 detector. The larger container needs a few cutouts to accommodate the RJ11 cables that link the weather stations to the BME280 and the longer cables that collect data from the rain and wind sensors.

Commercial enclosures typically feature cable routing holes and watertight grommets. Alternatively, make your own holes and secure the cables using grommets and sealing glands.

You may use this 3-dimension printable mount to safely store your Raspberry Pi within one of the recommended enclosures mentioned at the beginning of the article. The BME280 mounting bracket ought to snap in place.

You can fasten the mounts to the larger box by driving short self-tapping screws through the holes and/or grooves in its back.

Air circulation surrounding the BME280 sensor is required for accurate environmental temperature and humidity readings. Take out one side's worth of hole covers from the smaller box. The sensor's wires can then be threaded vertically via a single opening. If you mount this outside, make sure the holes drain away from the box.

If you want to keep water out of the enclosure, which could damage your cables, use watertight nylon cable glands. In the event that the glands do not entirely enclose the cables, you may use grommets that you have 3D printed or electrical tape to provide a more secure fit.

The larger enclosure is suggested, containing rubber plugs in each of the four openings. Make sure your cables have somewhere to go by drilling three holes in the base of the box. Put an M16 cable gland in the outside holes, then thread the cables for the rain gauge and wind sensors through them.

The Ethernet cable can connect your weather station to a wired network, but you may need to drill a more prominent gland or an additional hole in the enclosure to accommodate the cable.

Finally, run the cord for the power supply, the DS18B20 probe, and the BME280 sensor's wires through the more prominent M20 gland you've positioned above the center hole.

Given the size of the M20's hole, it's vital to use cable pads to guarantee a snug connection.

When the larger box is housed indoors, it is protected from the elements, and it is also much simpler to plug into an electrical outlet and set up a network. A larger opening in an exterior wall may be necessary to accommodate the cables needed to connect the external sensors. When everything is mounted externally, only the weather station requires electricity.

Prepare the weather station for its outdoor installation. You may install your station anywhere, from a wall or roof to a fence or underground pipe. The sensors can be installed anywhere there is access to the outdoors. In particular, remember the following:

• Rain must fall into the rain gauge for it to work.

• Both the anemometer and the wind vane must be exposed to wind.

• Keep the smaller BME280 box out of the sun, as it requires ventilation.

• Both power and internet access are required for the weather station to function.

Since the manner of mounting your station will vary depending on your location and environment, we cannot provide detailed instructions. But here are some pointers for getting started with a handful of the process's aspects:

## Conclusion

In conclusion, if you want to learn more about the Internet of Things and weather monitoring technologies, assembling your own IoT-based weather station using a Raspberry Pi 4 is a fantastic way. This task is manageable and can be modified to meet your requirements.

You'll need a Raspberry Pi 4 and sensors to detect things like temperature, humidity, and barometric pressure, as well as a way to get the data from the sensors and into the cloud for analysis. A dependable and accurate weather station may be set up with the help of the project instruction and the programming language python.

After setting up your IoT-based weather station, you can access accurate, up-to-the-minute forecasts from any location. Agricultural, transportation, and emergency response applications are just some of this data's possible uses.

Building an Internet of Things (IoT) weather station with a Raspberry Pi 4 is an exciting and instructive way to gain practical expertise in weather monitoring. You can construct and deploy a reliable weather station that serves your needs by following the recommended procedures and using the recommended resources. The following tutorial will teach how to Interface the weight sensor HX711 with raspberry pi 4.