Skip to content

Latest commit

 

History

History
197 lines (143 loc) · 8.76 KB

README.md

File metadata and controls

197 lines (143 loc) · 8.76 KB

Overview

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 app
  • station - Command line utility for creating and managing registered weather stations. See station --help for more information
  • measurement - 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.

Development Setup

  • 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

Building executables

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/.

Production build

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.

Production setup

Using NGINX

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.

Using Systemd

  • Create a new service config file: /etc/systemd/system/weather-api.service changing weather-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

Port forwarding

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

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

Sending test measurements

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.

Parsing measurement data

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.