Weather station with ModBus over RS-485
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

913 lines
36 KiB

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<meta name="author" content="M.T. Konstapel" />
<meta name="dcterms.date" content="2024-01-22" />
<title>Weather station</title>
<link rel="stylesheet" href="./css/mvp.css" />
<style type="text/css">
:root {
--width-content: 1080px;
}
nav {
justify-content: space-around;
}
</style>
</head>
<body>
<header id="title-block-header">
<nav id="TOC">
<ul>
<li>
<a href="#">Index</a>
<ul>
<li><a href="#why-do-you-need-a-weather-station"
id="toc-why-do-you-need-a-weather-station">Why do you need a
weather station?</a></li>
<li><a href="#what-should-a-weather-station-measure"
id="toc-what-should-a-weather-station-measure">What should a
weather station measure?</a></li>
<li><a href="#what-sensors-do-we-need"
id="toc-what-sensors-do-we-need">What sensors do we
need?</a></li>
<li><a
href="#what-to-use-for-communication-with-the-outside-world"
id="toc-what-to-use-for-communication-with-the-outside-world">What
to use for communication with the outside world?</a></li>
<li><a href="#what-else" id="toc-what-else">What
else?</a></li>
<li><a href="#theory-of-operation---hardware"
id="toc-theory-of-operation---hardware">Theory of operation
- Hardware</a></li>
<li><a href="#theory-of-operation---software"
id="toc-theory-of-operation---software">Theory of operation
- Software</a></li>
<li><a href="#prototype"
id="toc-prototype">Prototype</a></li>
<li><a href="#specifications"
id="toc-specifications">Specifications</a></li>
<li><a href="#schematic"
id="toc-schematic">Schematic</a></li>
<li><a href="#software-dependencies"
id="toc-software-dependencies">Software
dependencies</a></li>
<li><a href="#license" id="toc-license">License</a></li>
</ul>
</li>
<li>
<a href="./weather_station.pdf">PDF version</a>
</li>
<li>
<a href="https://git.meezenest.nl/">Git repo</a>
</li>
<li>
<a href="https://meezenest.nl/mees/">Back</a>
</li>
</ul>
<a href="https://www.meezenest.nl/mees/"><img alt="Logo" src="./images/mees_logo.svg" height="70"></a>
</nav>
<h1 class="title">Weather station</h1>
<p class="subtitle">with ModBus RTU interface</p>
<p class="author">M.T. Konstapel</p>
<p class="date">2024-01-22</p>
<p><a href="./weather_station.pdf"><i>PDF version</i></a></p>
</header>
<main>
<article>
<p><b>Abstract </b><p>A weather station build around a SparkFun Weather
Meter Kit (SEN-15901). The temperature, humidity and pressure are
measured with I2C sensors housed in an RS1 Passive Radiation Shield from
Garni. The data can be read via an RS485 ModBus RTU interface. The main
processor is an Arduino Pro Mini (ATmega328P 5V@16MHz)</p></p>
<h1 id="why-do-you-need-a-weather-station">Why do you need a weather
station?</h1>
<p>Well, you don’t…because if you want to know the weather, you look on
your phone. So why bother than? Because since the beginning of time,
people are obsessed with the weather. When I was a child, my grandmother
was measuring the temperature and rainfall on a daily basis. My
grandfather had an allotment, so he also was very interested in the
weather. The first thing my father read in the newspaper was the weather
report and the last thing he watched on the television was… the weather
report. And every hour he listed to the weather report on the radio. If
he talked to someone he always started the conversation by talking about
the weather. And when I open a new browser window, it automatically
opens the weather page.</p>
<p>So the weather is fascinating and taking your own measurements is a
lot of fun.</p>
<h1 id="what-should-a-weather-station-measure">What should a weather
station measure?</h1>
<p>As my grandmother already measured temperature and rainfall, these
ones are mandatory. And for the rest I looked at the website of the
Dutch meteorological institute. They measure wind direction, average
wind speed of the last 10 minutes, maximum wind gust of the last 10
minutes, rainfall of the last hour as well as the last 24 hours,
temperature, humidity and atmospheric pressure.</p>
<h3 id="measurements">Measurements</h3>
<ul>
<li>Wind direction</li>
<li>Wind speed (average of last 10 minutes)</li>
<li>Wind gust (last 10 minutes)</li>
<li>Rain fall (last hour)</li>
<li>Rain fall (last 24 hours)</li>
<li>Temperature</li>
<li>Humidity</li>
<li>Atmospheric pressure</li>
</ul>
<h1 id="what-sensors-do-we-need">What sensors do we need?</h1>
<h2 id="wind-and-rain">Wind and rain</h2>
<p>Measuring wind and rain is difficult. Well, not if you want to do it
by hand: place a beaker on the ground and wait a day. Than measure the
amount of water in it. Empty the beaker and start again. And for the
wind, you can stick a pole in the ground and attach a ribbon to it. The
direction of the wind can than be made visible. And even the wind speed
can be determent by measuring the angle between the ribbon and the
ground.</p>
<p>But how to do this automatically? Of course you can buy a fancy
commercial weather station. These are surprisingly cheap these days. But
that’s not a challenge. Besides, than you buy into a proprietary
ecosystem. And it probably only works when connected to the cloud. No
thanks!</p>
<p>Building from scratch is an option, but I am an electronic engineer,
not a mechanical one. I can imagine that won’t be a success. Besides
going the professional route, which is ridiculously expensive, there is
really only one option left: the SparkFun SEN-15901 Weather Meter.</p>
<figure>
<img src="./images/SparkFun-Weather_Meter.jpg"
title="SparkFun Weather Meter" alt="SparkFun Weather Meter" />
<figcaption aria-hidden="true">SparkFun Weather Meter</figcaption>
</figure>
<p>But this contraption does not come with any signal conditioning. We
have to make some kind of interface. Luckily, Sparkfun provides an
Arduino library, so we only have to connect the SEN-15901 to an Arduino
and run the code.</p>
<h2 id="temperature-humidity-and-air-pressure">Temperature, humidity and
air pressure</h2>
<p>These three are easy: there are a lot of I2C chips capable of
measuring these parameters. I choose the Silicon Labs Si7021 for
humidity and temperature and the Bosch BMP280 for pressure. Just hook
them up to the Arduino’s I2C bus, load the available libraries and Bob’s
your uncle.</p>
<p>To mount these sensors on the same mast as the SparkFun weather meter
I use the RS1 passive radiation shield from Garni.</p>
<figure>
<img src="./images/garni_rs1.jpg"
title="Garni RS1 Passive Radiation Shield"
alt="Garni RS1 Passive Radiation Shield" />
<figcaption aria-hidden="true">Garni RS1 Passive Radiation
Shield</figcaption>
</figure>
<h3 id="sensors">Sensors</h3>
<ul>
<li>SparkFun SEN-15901 Weather Station</li>
<li>Silicon Labs Si7021</li>
<li>Bosch BMP280</li>
</ul>
<h1 id="what-to-use-for-communication-with-the-outside-world">What to
use for communication with the outside world?</h1>
<p>Most consumer grade weather stations (and almost all other consumer
grade goods for that matter) use proprietary interfaces and protocols.
Probably to annoy the more technical skilled customer as you are not
able to interface these devices with other brands or self build systems.
I really hate that practice, so I won’t do that. Instead I will
implement a ModBus RTU interface. Dating back to 1979, this is the
industrial standard for communication between devices. And if the
professionals all use it, why not use it for this weather station?</p>
<h2 id="modbus">ModBus</h2>
<p>ModBus is a client/server data communications protocol in the
application layer of the OSI model. ModBus can work over several
different physical interfaces. For this application I will use an RS-485
interface. This interface is easy to implement and cables can be very
long, making it easy to locate the weather station. ModBus is a
lightweight protocol which can comfortably fit inside an under-powered
micro-controller like an Atmel ATmega328P. A simple RS-485 to USB dongle
connected to a PC is all you need to read the values from the weather
station.</p>
<h1 id="what-else">What else?</h1>
<p>Not much to be honest. Almost everything can be done in software. Of
course we need a power supply. And preferably a reverse polarity
protection. An input voltage of 12 Volt is convenient. 12 Volt power
bricks can be found in every charity shop and you can also use a 12 Volt
lead acid or lithium battery to power the weather station.</p>
<h1 id="theory-of-operation---hardware">Theory of operation -
Hardware</h1>
<figure>
<img src="./images/block_diagram.svg"
title="Block diagram of weather station"
alt="Block diagram of weather station" />
<figcaption aria-hidden="true">Block diagram of weather
station</figcaption>
</figure>
<h2 id="wind-speed">Wind speed</h2>
<p>Measuring the wind speed is done by a cup anemometer. It consisted of
three or four hemispherical cups on horizontal arms mounted on a
vertical shaft. The air flow past the cups in any horizontal direction
turned the shaft at a rate roughly proportional to the wind’s speed.
Every rotation, a magnet passes alongside a reed switch. The rate at
which the reed switch opens en closes is a measure of the wind
speed.</p>
<figure>
<img src="./images/sparkfun_cup_anemometer.jpg" title="Cup anemometer"
alt="Cup anemometer" />
<figcaption aria-hidden="true">Cup anemometer</figcaption>
</figure>
<p>By connecting one side of the reed switch to ground and the other via
a pull-up resistor to the supply voltage the mechanical switching action
is translated to an electrical pulse signal. This pulse can be read by a
micro-controller.</p>
<figure>
<img src="./images/diagram_cup_anemometer.svg"
title="Cup anemometer: theory of operation"
alt="Cup anemometer: theory of operation" />
<figcaption aria-hidden="true">Cup anemometer: theory of
operation</figcaption>
</figure>
<h2 id="wind-direction">Wind direction</h2>
<p>Measuring the wind direction is done by a wind vane. It consists of a
vertical blade mounted on a vertical shaft. Because the blade can turn,
it will always find the position of the least air resistance. The shape
of the blade is chosen so that it will always points directly to the
wind. A magnet mounted on the shaft rotates past several reed switches.
The switch that is closest to the magnet will close. If the magnet is
precisely between two reed switches both switches will close increasing
the resolution of the wind vane.</p>
<figure>
<img src="./images/sparkfun_wind_vane.jpg" title="Wind vane"
alt="Wind vane" />
<figcaption aria-hidden="true">Wind vane</figcaption>
</figure>
<p>Each reed switch is connected to a resistor and every resister has a
different value. The total resistance of the network will change
according to the wind direction. By connecting one side of the network
to ground and the other side via a resistor to VCC, a resisive divider
in made. This resistive divider converts the variable resistance to an
analog voltage which can be sampled by the A/D converter of a
micro-controller.</p>
<figure>
<img src="./images/diagram_wind_vane.svg"
title="Wind vane: theory of operation"
alt="Wind vane: theory of operation" />
<figcaption aria-hidden="true">Wind vane: theory of
operation</figcaption>
</figure>
<table>
<thead>
<tr class="header">
<th>Direction</th>
<th>Resistance</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td></td>
<td>33kΩ</td>
</tr>
<tr class="even">
<td>22.5°</td>
<td>6.57kΩ</td>
</tr>
<tr class="odd">
<td>45°</td>
<td>8.2kΩ</td>
</tr>
<tr class="even">
<td>67.5°</td>
<td>891Ω</td>
</tr>
<tr class="odd">
<td>90°</td>
<td>1kΩ</td>
</tr>
<tr class="even">
<td>112.5°</td>
<td>688Ω</td>
</tr>
<tr class="odd">
<td>135°</td>
<td>2.2kΩ</td>
</tr>
<tr class="even">
<td>157.5°</td>
<td>1.41kΩ</td>
</tr>
<tr class="odd">
<td>180°</td>
<td>3.9kΩ</td>
</tr>
<tr class="even">
<td>202.5°</td>
<td>3.14kΩ</td>
</tr>
<tr class="odd">
<td>225°</td>
<td>16kΩ</td>
</tr>
<tr class="even">
<td>247.5°</td>
<td>14.12kΩ</td>
</tr>
<tr class="odd">
<td>270°</td>
<td>120kΩ</td>
</tr>
<tr class="even">
<td>292.5°</td>
<td>42.12kΩ</td>
</tr>
<tr class="odd">
<td>315°</td>
<td>64.9kΩ</td>
</tr>
<tr class="even">
<td>337.5°</td>
<td>21.88kΩ</td>
</tr>
</tbody>
</table>
<h2 id="rain-fall">Rain fall</h2>
<p>Measuring the amount of rain fall is done by a self-emptying tipping
bucket. Rainwater is collected and funneled to a tipping bucket. The
bucket tips over when a certain amount of water is collected. The bucket
drains and a second bucket is automatically placed under the funnel.
When a certain amount of water is collected in this second bucket it
will tip over and the first bucket is raised again.</p>
<figure>
<img src="./images/sparkfun_rain_meter_inside.jpg" title="Rain meter"
alt="Rain meter" />
<figcaption aria-hidden="true">Rain meter</figcaption>
</figure>
<p>Every time the bucket tips over a magnet passes by a reed switch,
which closes and opens again. As with the cup anemometer this mechanical
movement can be translated to an electrical pulse by connecting one side
of the switch to ground and the other side via a pull-up resistor to
VCC. This pulse can than be read by a micro-controller.</p>
<p><sup>NOTE</sup> The rain meter is very sensitive: even a small amount
of movement and the bucket tips over. Mounting the rain meter in the
mast together with the wind meters can cause false triggers from the
rocking motion of the mast.</p>
<figure>
<img src="./images/diagram_rain_meter.svg"
title="Rain meter: theory of operation"
alt="Rain meter: theory of operation" />
<figcaption aria-hidden="true">Rain meter: theory of
operation</figcaption>
</figure>
<h2 id="humidity">Humidity</h2>
<p>Measuring the relative humidity is done by an electronic sensor based
on capacitive sensing using polymeric dielectrics. The humidity sensor
is a small capacitor consisting of a hygroscopic dielectric material
placed between a pair of electrodes. Absorption of moisture by the
sensor results in an increase in sensor capacitance. The opposite is
also true: when the moisture disappears, sensor capacitane decreases.
There is a direct relationship between relative humidity, the amount of
moisture present in the sensor, and the sensor capacitance. The relative
humidity is defined as the ratio of the amount of water vapor in the air
at a specific temperature to the maximum amount that the air could hold
at that temperature, expressed as a percentage. As the humidity sensor
has a build in temperature sensor, it can calculate the relative
humidity.</p>
<figure>
<img src="./images/capacitive_humidity_sensor.png"
title="Humidity sensor" alt="Humidity sensor" />
<figcaption aria-hidden="true">Humidity sensor</figcaption>
</figure>
<p>The Si7021 humidity sensor has an I²C bus for communication with a
micro-controller.</p>
<figure>
<img src="./images/Si7021_block_diagram.png"
title="Humidity sensor: block diagram"
alt="Humidity sensor: block diagram" />
<figcaption aria-hidden="true">Humidity sensor: block
diagram</figcaption>
</figure>
<h2 id="temperature">Temperature</h2>
<p>Measuring the temperature is done by the build in temperature sensor
of the humidity sensor. This sensor is used by the humidity sensor to
calculate the relative humidity. But as this sensor is very accurate it
can be used for ambient temperature measurments.</p>
<h2 id="atmospheric-pressure">Atmospheric pressure</h2>
<p>Measuring the atmospheric pressure is done by an electronic sensor
based on a piezo-resistive pressure sensing element. The piezo-resistive
effect is a change in the electrical resistivity of a semiconductor or
metal when mechanical strain is applied. In this case the strain comes
from the atmospheric pressure. The sensor measures the resistance which
is proportional to the atmospheric pressure.</p>
<figure>
<img src="./images/piezo_resistive_pressure_sensor.png"
title="Piezo-resistive pressure sensor"
alt="Piezo-resistive pressure sensor" />
<figcaption aria-hidden="true">Piezo-resistive pressure
sensor</figcaption>
</figure>
<p>The BMP280 pressure sensor has an on board temperature sensor which
can also be used to measure the ambient temperature. As this sensor is
less accurate compared to the sensor of the Si7021 humidity sensor, the
sensor is only used as a backup sensor. The BMP280 has an I²C bus for
communication with a micro-controller.</p>
<figure>
<img src="./images/BMP280_block_diagram.png"
title="Pressure sensor: block diagram"
alt="Pressure sensor: block diagram" />
<figcaption aria-hidden="true">Pressure sensor: block
diagram</figcaption>
</figure>
<h2 id="illumination">Illumination</h2>
<p>This sensor is still under development.</p>
<h2 id="modbus-interface">ModBus interface</h2>
<p>The RS-485 interface is build with a MAX485E driver chip from Maxim
Integrated. Nothing much to say as the implementation is pretty much
following the typical application from the datasheet.</p>
<figure>
<img src="./images/rs-485.svg" title="RS-485 interface"
alt="RS-485 interface" />
<figcaption aria-hidden="true">RS-485 interface</figcaption>
</figure>
<p>If the device is the first or last device on the RS-485 bus, a 120
Ohm termination resistor can be enabled by placing a jumper on header
J7.</p>
<p>The Arduino micro-controller can be programmed via an in circuit
programmer, which shares the serial port with the MAX485E. Resistor R5
isolates the output of the MAX485 from the signal of the programmer.</p>
<h2 id="i²c-bus">I²C bus</h2>
<p>The I²C bus is integrated in the micro-controller. But because the
micro-controller uses a power supply of 5 Volt and the I²C sensors use
3.3 Volt a bidirectional level shifter is needed. This way the sensors
can be used on the 5V I²C bus without the risk of damaging the
sensors.</p>
<figure>
<img src="./images/i2c_bus.svg" title="I²C bus level shifter"
alt="I²C bus level shifter" />
<figcaption aria-hidden="true">I²C bus level shifter</figcaption>
</figure>
<p>Let’s assume the I²C signal lines on either end of the MOSFETs are
either outputting a logic high or is configured as an input. Effectively
this means there is nothing pulling the signal levels down.</p>
<p>The voltage between the gate and source of both MOSFETs is at 0V
(both are at 3.3V) so the MOSFET is switched off. Therefore both sides
of the MOSFETs are logic high.</p>
<p>When either of the 3.3 Volt signal lines outputs a logic low the
corresponding drain is pulled to ground. Now the voltage between the
gate and the source is 3.3V and the MOSFET turns on causing the 5 Volt
side to go low as well.</p>
<p>When either of the 5 Volt signal lines outputs a logic low the body
diode of the corresponding MOSFET start conducting, causing the source
voltage to drop below the gate voltage. The MOSFET switches on and the
3.3 volt side goes low.</p>
<h2 id="power-supply">Power supply</h2>
<p>Typical, a 12 Volt power supply is used to power the device, but it
can be powered from a wide range of voltages, from 6.5 to 36 Volt. A
switching regulator (U3) supplies the 5 Volt power rail and a linear low
drop regulator (U4) supplies the 3.3 volt power rail.</p>
<figure>
<img src="./images/power_supply.svg" title="Power supply"
alt="Power supply" />
<figcaption aria-hidden="true">Power supply</figcaption>
</figure>
<h3 id="input-protection">Input protection</h3>
<p>C1 and C5 short out high frequency signals, protecting the input from
ESD. Bidirectional transient-voltage-suppression diodes D1 end D2 clamp
transient voltages, again protecting the input from ESD.</p>
<p>And than Q3 and its surrounding components: this is the reverse
polarity protection. Usually, a series diode is used, but due to the
voltage drop across such a diode it dissipates energy which is wasteful.
The circuit with Q3 on the other hand has a very low voltage drop
resulting in an almost zero loss solution.</p>
<p>If VCC is applied in the correct polarity, the source will
immediately rise to the about VCC because of the body diode
conducting.</p>
<p>The gate will charge towards -VCC with respect to the source through
R1. When the gate reaches the threshold voltage the MOSFET channel will
begin to conduct, and by the time the gate-source voltage reaches a few
volts the MOSFET channel will be conducting almost all the current, the
output voltage will be close to VCC. It continues to charge until it
reaches about -10V at which point the zener diode begins to shunt
significant current away from the gate.</p>
<p>In steady state with VCC on the drain the gate sits at -10V with
respect to the source, and the MOSFET happily conducts in the reverse
direction.</p>
<p>When VCC is applied in the reverse polarity, the body diode of the
MOSFET cannot conduct. Only a small leakage current can flow from the
source to the drain via resistor R1 and zener diode D3, which now acts
as a normal diode. The gate and the source are now at almost the same
potential and the MOSFET cannot conduct, protecting the device from
reverse polarity.</p>
<figure>
<img src="./images/reverse_polarity_protection.svg"
title="Reverse polarity protection" alt="Reverse polarity protection" />
<figcaption aria-hidden="true">Reverse polarity protection</figcaption>
</figure>
<h2 id="microcontroller">Microcontroller</h2>
<p>The heart of the circuit is an Arduino Pro Mini, which is basically
an Atmel ATmega328P with a special Arduino bootloader, making it an easy
platform for developing software.</p>
<figure>
<img src="./images/micro-controller.svg" title="Microcontroller"
alt="Microcontroller" />
<figcaption aria-hidden="true">Microcontroller</figcaption>
</figure>
<p>Both the signals from the rain meter and the cup anemometer are
connected to interrupt pins of the micro-controller. The signal from the
rain meter is lightly filtered by C8.</p>
<p>The ModBus address can be set by DIP switch J9.</p>
<h1 id="theory-of-operation---software">Theory of operation -
Software</h1>
<h2 id="wind-speed-1">Wind speed</h2>
<p>The pulse from the cup anemometer is connected to an interrupt input
of the micro-controller. Every time its logic level changes an interrupt
routine is called. This routine increments a counter and checks how many
time has passed since the previous interrupt call. If the previous call
was more than 6 seconds ago, the wind speed is (almost) zero. If the
previous call was just over 3 second ago the interrupt counter now holds
the amount of pulses in three seconds. This value is stored and from
that value the wind speed can be calculated. If the previous call was
less than 3 seconds ago the measurement is still in progress and no
further action is taken. A measurment period of three seconds is choosen
because it is the standard as used by the Dutch meteorological institute
(KNMI).</p>
<figure>
<img src="./images/wind_speed_diagram.svg" title="Wind speed interrupt"
alt="Wind speed interrupt" />
<figcaption aria-hidden="true">Wind speed interrupt</figcaption>
</figure>
<h2 id="wind-direction-1">Wind direction</h2>
<p>The analog signal from the wind vane is fed into the analog to
digital converter of the micro-controller. The software samples this
signal and determines which value from a lookup table is closest to the
value from the ADC. The lookup table now gives the wind direction in
degrees.</p>
<figure>
<img src="./images/wind_direction_diagram.svg"
title="Getting the wind direction" alt="Getting the wind direction" />
<figcaption aria-hidden="true">Getting the wind direction</figcaption>
</figure>
<p>As the tolerances between micro-controllers can be high, the wind
vane has to be calibrated in order to get a correct lookup table.</p>
<h2 id="rain-fall-1">Rain fall</h2>
<p>The pulse from the rain meter is connected to an interrupt input of
the micro-controller. Every time a rising edge is detected an interrupt
routine is called. This routine debounces the signal and increments the
rain counter. This counter can be used to calculate the rain fall.</p>
<h2 id="humidity-1">Humidity</h2>
<p>Via the I²C bus, the humidity value of the sensor is read. As the
sensor can become saturated with moisture it can get stuck at 100%. This
happens in particular with fog or other high humidity and condensing
weather types. The sensor has a build in heater to drive of moisture and
thus preventing this problem. Because the temperature of the sensor
rises when the heater is turned on, accurate ambient temperature and
humidity readings are no longer possible. But with a smart algorithm it
is possible to get the benefits of the build in heater while still being
able to use the sensor as an ambient thermometer.</p>
<p>When the humidity rises above 95% for more than an hour the current
temperature and humidity are stored and the heater is switched on for 5
minutes. Than the heater is switched off again. If after 15 minutes the
humidity is still above 95% the heater is turned on again for another 5
minutes. But not before the temperature and humidity are measured and
stored, as the sensor is now cooled off to ambient temperature. If the
humidity is below 95% the sensor is free from moisture and the process
is not repeated for another hour.</p>
<p>When the heater algorithm is active, the temperature and humidity
values are updated every 20 minutes instead of every 2 seconds. Statis
bit 0 (ModBus register 30014) indicated if the heater is on or off and
status bit 1 gives the update rate of the temperature and humidity
values.</p>
<p>This algorithm can be enabled by setting the HeaterCoil (see ModBus
secion).</p>
<figure>
<img src="./images/smart_heater.svg" title="Heater algorithm"
alt="Heater algorithm" />
<figcaption aria-hidden="true">Heater algorithm</figcaption>
</figure>
<h2 id="temperature-1">Temperature</h2>
<p>The temperature is read from the humidity sensor as this sensor gives
the most accurate temperature readings. When the heater is on (see
section humidity above) the temperature readings are temporary stopped
and only updated every 20 minutes. As a backup, the slightly less
accurate temperature readings from the pressure sensor can be used.</p>
<h2 id="atmospheric-pressure-1">Atmospheric pressure</h2>
<p>Via the I²C bus, the atmospheric pressure value of the sensor is
read. There is nothing further to say about this sensor: it is rather
boring.</p>
<h2 id="illumination-1">Illumination</h2>
<p>This sensor is still under development.</p>
<h2 id="modbus-interface-1">ModBus interface</h2>
<p>The weather station uses ModBus RTU over a simplex RS-485 line. For
now, the ModBus address is hard coded as 14 in the software. The values
are available in the input registers and can be read via function code
04.</p>
<p>Below an example of how to read the wind direction and enable the
heater algorithm in Python using the minimalmodbus library.</p>
<pre><code>#!/usr/bin/env python3
import minimalmodbus
# port name, slave address (in decimal)
instrument = minimalmodbus.Instrument(&#39;/dev/ttyUSB1&#39;, 14)
# register number, number of decimals, function code
wind_direction = instrument.read_register(1, 0, 4)
print(wind_direction)
# register address, value, function code
instrument.write_bit(0, 1, 5)</code></pre>
<h3 id="input-registers-read-only">Input registers (read only)</h3>
<p>Input registers are numbered 30001 to 39999 but have data addresses
0x000 to 0x270E. The measurements and order of the measurements are the
same as for APRS weather reports. But of course we use SI units.</p>
<table>
<colgroup>
<col style="width: 13%" />
<col style="width: 53%" />
<col style="width: 33%" />
</colgroup>
<thead>
<tr class="header">
<th>Address</th>
<th>Description</th>
<th>Units</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>00</td>
<td>Device ID (0x5758)</td>
<td>NO UNIT</td>
</tr>
<tr class="even">
<td>01</td>
<td>Wind direction</td>
<td>degrees * 10</td>
</tr>
<tr class="odd">
<td>02</td>
<td>Wind speed (average of 10 minutes)</td>
<td>m/s * 100</td>
</tr>
<tr class="even">
<td>03</td>
<td>Wind gust (peak of last 10 minutes)</td>
<td>m/s * 100</td>
</tr>
<tr class="odd">
<td>04</td>
<td>Temperature (two’s complement)</td>
<td>degrees Celcius * 100</td>
</tr>
<tr class="even">
<td>05</td>
<td>Rain last hour</td>
<td>l/m2 * 100</td>
</tr>
<tr class="odd">
<td>06</td>
<td>Rain last 24 hours</td>
<td>l/m2 * 100</td>
</tr>
<tr class="even">
<td>07</td>
<td>Rain since midnight</td>
<td>NOT IMPLEMENTED</td>
</tr>
<tr class="odd">
<td>08</td>
<td>Humidity</td>
<td>percent * 100</td>
</tr>
<tr class="even">
<td>09</td>
<td>Barometric pressure</td>
<td>hPa * 10</td>
</tr>
<tr class="odd">
<td>10</td>
<td>Luminosity</td>
<td>W/m2</td>
</tr>
<tr class="even">
<td>11</td>
<td>Snow fall</td>
<td>NOT IMPLEMENTED</td>
</tr>
<tr class="odd">
<td>12</td>
<td>Raw rain counter</td>
<td>l/m2 * 100</td>
</tr>
<tr class="even">
<td>13</td>
<td>Temperature (two’s complement)</td>
<td>degrees Celcius * 100</td>
</tr>
<tr class="odd">
<td>14</td>
<td>Status bits</td>
<td>see table below</td>
</tr>
</tbody>
</table>
<p><sup>NOTE</sup> Register 13 holds the backup temperature reading from
the pressure sensor.</p>
<table>
<colgroup>
<col style="width: 18%" />
<col style="width: 31%" />
<col style="width: 25%" />
<col style="width: 24%" />
</colgroup>
<thead>
<tr class="header">
<th>Status bits</th>
<th>Description</th>
<th>logic 0</th>
<th>logic 1</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>0</td>
<td>Heater status</td>
<td>heater off</td>
<td>heater on</td>
</tr>
<tr class="even">
<td>1</td>
<td>Temp/humidity update</td>
<td>every 20 minutes</td>
<td>every 2 seconds</td>
</tr>
<tr class="odd">
<td>2</td>
<td>Heater algorithm</td>
<td>disabled</td>
<td>enabled</td>
</tr>
</tbody>
</table>
<p>The ModBus registers are 16 bit wide. For better precision, some
units are scaled by a factor of 10 or 100. This way, values with up to
two decimal points can be stored as 16 bit integer values. Just divide
by 10 or 100 to get the floating point values.</p>
<h3 id="output-coils-write-only">Output coils (write only)</h3>
<p>Output coils registers are numbered 1 to 9999 but have data addresses
0x000 to 0x270E. The default value of a register is 0.</p>
<table>
<thead>
<tr class="header">
<th>Address</th>
<th>Description</th>
<th>logic 0</th>
<th>logic 1</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>0</td>
<td>Heater algorithm</td>
<td>disabled</td>
<td>enabled</td>
</tr>
</tbody>
</table>
<h1 id="prototype">Prototype</h1>
<p>I wanted to locate the weather station at about 100 meters from the
house. That meant that interfacing the weather station was not just a
matter of connecting a wire to it. And 100 meters is a bit much for a
wifi connection either. As I already had experience with LoRa I opted
for that. But not LoRaWAN, but LoRa APRS. This is a ham radio network
that I often use. I even run my own digipeater. So LoRa APRS it is.</p>
<figure>
<img src="./images/prototype_block_diagram.svg"
title="Block diagram of the prototype"
alt="Block diagram of the prototype" />
<figcaption aria-hidden="true">Block diagram of the
prototype</figcaption>
</figure>
<p>The weather station’s RS-485 interface is connected to a Raspberry Pi
Zero 2W running the aprx digipeater software, as well as some specially
written Python programs to interface the build in LoRa transceiver, the
GPS module and the weather station itself. Every 10 minutes the
digipeater will read the weather station’s registers and sends the data
as PE1RXF telemetry messages over the APRS network to a server, which
presents the data in a Grafana dashboard. The digipeater can also send
standardized APRS weather reports over the APRS network. But more about
this project can be found here: <a
href="https://www.meezenest.nl/mees-elektronica/RPi_LoRa_shield.html">https://www.meezenest.nl/mees-elektronica/RPi_LoRa_shield.html</a></p>
<figure>
<img src="./images/prototype_overview_small.jpg"
title="The prototype in the garden" alt="The prototype in the garden" />
<figcaption aria-hidden="true">The prototype in the garden</figcaption>
</figure>
<p>As a housing for the prototype, I used an old beehive. These are
weatherproof and I had one laying around.</p>
<figure>
<img src="./images/prototype_sensors_small.jpg"
title="Closeup of the sensors" alt="Closeup of the sensors" />
<figcaption aria-hidden="true">Closeup of the sensors</figcaption>
</figure>
<figure>
<img src="./images/prototype_inside_annotations_small.jpg"
title="Inside the beehive" alt="Inside the beehive" />
<figcaption aria-hidden="true">Inside the beehive</figcaption>
</figure>
<h1 id="specifications">Specifications</h1>
<h2 id="wind">Wind</h2>
<ul>
<li><p>Wind speed is measured by taking 3 second averages from the cup
anemometer and using these samples to calculate the average over a 10
minute periode.</p></li>
<li><p>Wind gust is measured by taking 3 second averages from the cup
anemometer.</p></li>
<li><p>Wind vane has 8 main directions and another 8 directions in
between. But these last do not have the same weight, eg. these positions
are not as likely to be measured as the main directions. This is due to
the construction of the wind vane: it has eight reed switches for the
main directions and if the wind direction happens to sit exactly in
between two reed switches, both switches are closed giving the extra 8
sub directions. Not great, but it is what it is…</p></li>
</ul>
<h2 id="rain">Rain</h2>
<pre><code>Resolution: 0.2794 mm/impulse</code></pre>
<h2 id="humidity-2">Humidity</h2>
<pre><code>Operating range : 0 - 100 % RH
Recommended range : 20 - 80 % RH
Accuracy : +/- 3 % RH (0-80 % RH)
+/- 4.5 % (max when &gt; 80 % RH)
Heater to drive of moisture (can be enabled via ModBus)</code></pre>
<h2 id="pressure">Pressure</h2>
<pre><code>Operating range : 300 - 1100 hPa
Accuracy : +/- 1.0 hPa (0 - 65 °C)
+/- 1.7 hPa (-20 - 0 °C)</code></pre>
<h2 id="temperature-2">Temperature</h2>
<pre><code>Main sensor
-----------
Operating range : -10 - 85 °C (typ)
-40 - 85 °C (max)
Accuracy : +/- 0.3 °C (typ)
+/- 0.4 °C (max)
+/- 0.5 °C (max when &lt; -10°C)
Backup sensor
-------------
Operating range : 0 - 65 °C (typ)
-40 - 85 °C (max)
Accuracy : +/- 0.5 °C (25 °C)
+/- 1.0 °C (0 - 65 °C)</code></pre>
<h2 id="modbus-1">ModBus</h2>
<pre><code>Physical : RS-485 simplex RTU
Settings : 9600 bd 8N1
Address : 14</code></pre>
<h1 id="schematic">Schematic</h1>
<p><a href="./images/weather_station_schematic.pdf"><img
src="./images/weather_station_schematic.svg" alt="Schematic" /></a></p>
<!---
# Bill of materials
# Component placement
# Cables and pinouts
-->
<h1 id="software-dependencies">Software dependencies</h1>
<ul>
<li>Arduino IDE</li>
</ul>
<h2 id="arduino-libraries">Arduino libraries</h2>
<ul>
<li>https://github.com/sparkfun/SparkFun_Weather_Meter_Kit_Arduino_Library</li>
<li>https://github.com/orgua/iLib</li>
<li>https://github.com/epsilonrt/modbus-arduino</li>
<li>https://github.com/epsilonrt/modbus-serial</li>
</ul>
<p>Libraries are included with the source code of this project</p>
<h1 id="license">License</h1>
<p>Copyright (C) 2023, 2024 M.T. Konstapel</p>
<p><a
href="https://meezenest.nl/mees/">https://meezenest.nl/mees/</a></p>
<p>The software is published as open-source software (GPL). The hardware
is published as open-source hardware (OSH).</p>
<h2 id="software">Software</h2>
<p>This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at your
option) any later version.</p>
<h2 id="hardware-and-documentation">Hardware and documentation</h2>
<p>This work is licensed under a Creative Commons Attribution-ShareAlike
4.0 International License.</p>
<hr>
</article>
</main>
<footer>
<p>&copy;
2024-01-22
M.T. Konstapel
<a href="https://meezenest.nl/mees/">https://meezenest.nl/mees/</a>
</p><p>This work is licensed under a <a rel="license" href="http://creativecommons.org/licenses/by-sa/4.0/">Creative Commons Attribution-ShareAlike 4.0 International License</a>.
</p>
</footer>
</body>
</html>