- Overview
- Development Setup
- Building executables
- Production build
- Production setup
- Migrations
- Sending test measurements
- Parsing measurement data
This crate provides 3 binaries used to serve and manage your weather data.
server
- The HTTP + UDP server that listens for station data and serves the JSON API for the web appstation
- Command line utility for creating and managing registered weather stations. Seestation --help
for more informationmeasurement
- A debug tool that simulates a weather station sending measurements. This can be handy for testing that your server is live and that your proxy and firewall settings are properly configured. See Sending test measurements for more information.
- Install PostgreSQL. Other databases are not supported.
- Completely optional, but for development purposes an API client to test HTTP requests such an Insomnia.
- From a terminal (or powershell for Windows users), install the diesel cli for database management:
cargo install diesel_cli
- Copy the example config and edit as needed:
cp .env.example .env
- Create and seed the database:
diesel setup
If you ran into an error creating the database, ensure your user has CREATEDB
permissions by altering their roles: ALTER USER username CREATEDB;
If the setup ran successfully, you can now compile and run the server:
cargo run
To just build the executable without running it you can use the build
command:
cargo build
The executable will now be written to the directory target/debug/
.
A binary can be generated by running:
cargo build --release
The difference between running cargo build
and cargo build --release
is that the --release
flag will optimize the build and output a more streamlined executable at the cost of taking longer to build.
This will output the binaries target/release/server
.
This binaries can be moved and renamed as desired.
To run the executables, you will still need the .env file present in the same directory you run the executables in or optionally pass those environment variables in through other means.
Assuming you've set up nginx on your server already, a simple upstream block and server block can be configured using any free port.
upstream weather_api_upstream {
server localhost:5334
fail_timeout=0;
}
server {
server_name weather-api.example.com;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://weather_api_upstream;
}
}
This will implicitly listen on the default HTTP port 80 externally, however you will likely want to set up HTTPS which can be done automatically if you use certbot to generate an SSL certificate.
Once you verify your server is working and you have installed certbot, the command certbot --installer nginx
will prompt you about automatically reconfiguring your nginx config to which HTTP traffic on port 80 will either redirect or be blocked in favor of HTTPS on port 443.
- Create a new service config file:
/etc/systemd/system/weather-api.service
changingweather-api
to the name you desire for the service. - Inside the config add the following, updating the WorkingDirectory path and the path to the executable in ExecStart to match where your main server binary resides:
[Unit]
Description=Rust weather station api server
After=network.target
[Service]
Type=simple
User=http
WorkingDirectory=/srv/weather-station
ExecStart=/srv/weather-station/api-server/target/release/server
[Install]
WantedBy=multi-user.target
- Ensure the User field matches a valid user on the system that you wish to run the service as
- Reload your services by running
systemctl daemon-reload
- Check your service file is throwing no errors:
systemctl status weather-api.service
- Start the service:
systemctl start weather-api.service
- Check again to ensure the server is running:
systemctl status weather-api.service
. Giving you an output like:
● weather-api.service - Rust weather station api server
Loaded: loaded (/etc/systemd/system/weather-api.service; disabled; vendor preset: disabled)
Active: active (running) since Fri 2021-05-21 11:55:03 EDT; 41min ago
Main PID: 1501185 (server)
Tasks: 10 (limit: 18784)
Memory: 3.5M
CGroup: /system.slice/weather-api.service
└─1501185 /srv/weather-station/api-server/target/release/server
May 21 11:55:03 Hubble systemd[1]: Started Rust weather station api server.
May 21 11:55:03 Hubble server[1501185]: [2021-05-21T15:55:03Z INFO actix_server::builder] Starting 4 workers
May 21 11:55:03 Hubble server[1501185]: [2021-05-21T15:55:03Z INFO server] Listening on UDP address 0.0.0.0:3381
May 21 11:55:03 Hubble server[1501185]: [2021-05-21T15:55:03Z INFO actix_server::builder] Starting "actix-web-service-0.0.0.0:5333" service on 0.0.0.0:5333
- Notice the service is started but not "enabled", meaning it won't auto-start on reboot. To enable it, run
systemctl enable weather-api.service
Assuming you have the HTTP socket connected via a reverse proxy, you will either need to use a UDP proxy for the UDP port or directly expose the UDP port through your firewall. The UDP port listens for incoming weather station measurements. Before configuring real weather station hardware to use the port, ensure you can successfully send data by using the provided measurements script to send test measurements (see below on how to use).
Migrations are a way of "migrating" an existing database schema to a new format.
So if new features are added or fields are renamed from one version of the app to the other, you can run migrations to preserve your existing data but update it to match the schema the current version of the app expects. For more information, go read up on schema migration.
Migrations already ran when the diesel setup
command was run, but if you are upgrading your version of the app then you can manually trigger migrations like so:
diesel migrate
If you wish to test the server's UDP port, use the provided measurement
script like so:
./target/release/measurement 127.0.0.1:3381 053050d3-7fa7-438d-93f1-8285fd5eef79 aV3!mIv9ci47y90Gqo air_temp=23.4,humidity=62.45
or via Cargo in development:
cargo run --bin measurement -- 127.0.0.1:3381 053050d3-7fa7-438d-93f1-8285fd5eef79 aV3!mIv9ci47y90Gqo air_temp=23.4,humidity=62.45
Run ./target/release/measurement --help
for more information.
A datagram consists of values separated by commas, where each key=value
pair represents a sensor and a measurement for that sensor.
Note that the sensor "keys", rather than being the sensors' UUIDs, are actually the sensors' alias
fields.
The purpose of the alias is to reduce the datagram size, keep the datagram human-readable, but more importantly make it easy to configure some common sensors you'll typically see on a weather station without having to tediously map each sensor UUID manually inside the stations' Arduino sketch files.
air_temp=23.4,humidity=62.45,id=053050d3-7fa7-438d-93f1-8285fd5eef79#540151989
The station id
is also sent as a key-value pair and is required so the server knows which station the measurements are coming from.
The end of the message is a hash symbol (#
) followed by a CRC hash.
This hash is generated from the full message (before the hash itself is added of course) and the station's secret key.
The purpose of appending this CRC hash is so the server can verify the message came from the weather station device it says it did.
Attempting to modify the message in any way will result in the hash being invalid and the server will discard the entire message.