How to build, wire, and flash the outdoor sensor unit, and the one field-calibration step (vane north alignment).
UNOFFICIAL — NOT FOR FLIGHT PLANNING. This device is not a certified instrument. See the README.
The outdoor unit is an ESP32 with three sensors:
| Sensor | Measures | Interface |
|---|---|---|
| BME280 | temperature, humidity, station pressure | I²C |
| GPS (e.g. NEO-6M) | latitude, longitude, altitude, satellites | UART (serial) |
| Anemometer | wind speed (pulses) + direction (vane) | digital pulse + analog (ADC) |
There is no light sensor — sky/cloud/UV come from the optional METAR feed instead, which is more reliable for a pilot than a backyard lux estimate. An optional SSD1306 OLED shows a local status page.
Anemometer choices:
- Primary — Davis 6410. Continuous-potentiometer direction (smooth 0–360°), rugged, reed-switch speed. The sketch's defaults are tuned for it.
- Budget — SparkFun Weather Meter Kit. Reed-switch speed + a resistor-network vane (8/16 discrete positions). Works fine with two changes noted below.
These are taken directly from sketches/outdoor.ino — they are the authoritative pinout. If you
re-pin, change the #defines in the sketch, not just this table.
| Function | ESP32 pin | Notes |
|---|---|---|
| BME280 SDA | GPIO 21 | I²C (Wire.begin(21, 22)), address 0x76 |
| BME280 SCL | GPIO 22 | I²C |
| GPS RX (ESP32 receives) | GPIO 16 | Serial2 @ 9600 baud |
| GPS TX (ESP32 sends) | GPIO 17 | Serial2 |
| Anemometer speed pulse | GPIO 25 | INPUT_PULLUP, FALLING-edge interrupt (WIND_SPEED_PIN) |
| Anemometer direction wiper | GPIO 34 | ADC1, 12-bit (WIND_DIR_PIN) |
| OLED (optional) | GPIO 21/22 | I²C, address 0x3C |
GPIO 34 is input-only with no internal pull — correct for the vane wiper. The speed pin uses the internal pull-up; the reed switch closes it to ground once per revolution.
Speed (pulse): the reed/hall switch connects GPIO 25 ↔ GND. Each closure is one count; the
firmware debounces (WIND_DEBOUNCE_US = 1000, ~1 ms) and converts pulse frequency to m/s.
Direction (Davis 6410, continuous pot): three wires — 3.3 V → pot high, wiper → GPIO 34,
GND → pot low. The firmware reads the ADC and maps it linearly to 0–360° in vaneDegrees().
Confirm the exact wire colors against your unit's datasheet before powering up — connector pinouts vary between revisions.
Speed-constant calibration (#define in the sketch):
#define WIND_MPH_PER_HZ 2.25f // Davis 6410 (per datasheet)- Davis 6410: leave at
2.25. - SparkFun Weather Meter: change to
1.492(≈ 2.4 km/h per Hz), and replace the linearvaneDegrees()map with a nearest-voltage lookup table — the SparkFun vane is a resistor network with discrete positions, not a continuous pot.
The firmware converts to m/s on the wire; the server converts to knots for display (knots are fixed, aviation-universal).
Good wind data wants the vane/cups high on a clear mast, away from building eddies. The BME280 wants shaded, ventilated air near the area of interest, and the GPS wants sky view. At many fields these can't all live in one spot — so airfield-wx supports three arrangements. The first two are the default (one device); the third is an opt-in for sites where wind and thermo can't co-locate.
| # | Topology | Devices | Wind source |
|---|---|---|---|
| 1 | All-in-one (default) | one ESP32: BME280 + GPS + anemometer | outdoor payload |
| 2 | Remote anemometer on a cable (default-equivalent) | one ESP32; anemometer high on a mast, wires run down to it | outdoor payload |
| 3 | Separate wind station (opt-in) | a second ESP32 + anemometer (no BME280, no GPS) at the good wind spot | the wind station |
Which one is right for you?
- Start with #1 (all-in-one) if you have a single good spot — a post or pole, reasonably clear of the hangar/house — where you can put everything together. This is most people. One device, one set of wires.
- Choose #2 (remote anemometer) if your thermometer spot is fine but the wind there is dirty (turbulence off a roof or trees). Mount just the wind cups/vane up high on a mast and run their cable back down to the same ESP32. From the software's point of view it's identical to #1 — there's nothing extra to configure.
- Choose #3 (separate wind station) only if good wind and good thermometer spots are genuinely far apart — e.g. the wind mast belongs at the edge of the strip but the temperature/GPS box lives by the hangar. You build a small second ESP32 with just an anemometer. It's more work and a second power feed, so don't reach for it unless #1/#2 can't give you clean wind.
The first two are firmware-identical — flash outdoor.ino and leave [wind] source = "outdoor" (the
default). Topology 3 adds a dedicated wind station running wind_station.ino; location intelligence
stays anchored to the outdoor unit's GPS (one field, one position), and you select it with
[wind] source = "<wind-station-id>" (see the install guide).
Whichever you pick, the wind becomes a live runway solution on the dashboard:
Which vane offset applies where. The wind_vane_offset_deg calibration (below) travels with the
device that physically holds the anemometer — the outdoor unit in topologies 1/2, the wind station
in topology 3. The server applies it to whichever device [wind] source names.
Anemometer-only — the same wind pins as the outdoor unit, nothing else:
| Function | ESP32 pin | Notes |
|---|---|---|
| Anemometer speed pulse | GPIO 25 | INPUT_PULLUP, FALLING-edge interrupt (WIND_SPEED_PIN) |
| Anemometer direction wiper | GPIO 34 | ADC1, 12-bit (WIND_DIR_PIN) |
Wiring, the speed constant (WIND_MPH_PER_HZ), and the Davis 6410 / SparkFun vane handling are
identical to the outdoor unit (above) — wind_station.ino reuses that anemometer code verbatim. It
has no BME280, GPS, or OLED.
The sketches are in sketches/:
outdoor.ino— the field unit (BME280 + GPS + anemometer). Flash with the Arduino IDE orarduino-cli(ESP32 board support + the BME280, TinyGPS, Adafruit SSD1306 libraries).wind_station.ino(topology 3 only) — the separate wind station (anemometer only). Needs only the ESP32 core libraries (no BME280/TinyGPS/SSD1306).
Before flashing either, edit the top of the file:
ssid/password— your Wi-Fi credentials.- The static IP block (
ip,gateway,subnet,dns) — pick a LAN address and record it; you'll put it inserver/weather.tomlas that sensor'sip.wind_station.inodefaults to192.168.1.61so it doesn't collide with the outdoor unit's192.168.1.60— change both to suit your LAN.
After flashing, browse to http://<sensor-ip>/data:
- outdoor → a JSON object with
temperatureC,pressure,windSpeed,windDirection,latitude, etc. - wind station → an anemometer-only object:
windSpeed,windGust,windDirection, plusrssi,uptime,freeHeap(no temperature/pressure/GPS keys).
Mount the anemometer clear of roof/hangar turbulence — a vane in a building eddy reads a plausible but wrong direction, which is the classic failure mode (see ADR-0003). Open exposure, standard height, away from obstructions.
The vane's "zero" rarely points at true north when mounted. Rather than physically re-aiming it, you
record a software offset once. The offset is applied server-side (wind_vane_offset_deg, like the
temperature offset), so the firmware always emits the raw vane reading.
Procedure:
- With the server running, hold or rotate the vane to a known true bearing — e.g. line it up due east (090° true) using a sighting compass corrected for local variation, or align it down a runway whose true heading you know.
- Read the raw vane reading from the API:
curl -s http://<server>:8005/api/v1/current \ | python3 -c "import json,sys; print(json.load(sys.stdin)['sensors']['outdoor']['raw']['wind_direction_deg'])"
- Compute the offset = (true bearing − raw reading), normalized into 0–360. Hypothetical: if you aimed it at 090° true and the raw reading is 075°, set the offset to +15.
- Put it in
server/weather.tomlunder the outdoor sensor:[[sensors]] id = "outdoor" # ... wind_vane_offset_deg = 15.0
- Restart the server and confirm
derived.wind_direction_true_degnow reads the true bearing you set (it equalsraw.wind_direction_deg + wind_vane_offset_deg, normalized).
That corrected true direction is what feeds the runway crosswind/headwind solution; the dashboard also shows the magnetic equivalent using the WMM variation for your field.
Next: Install & configuration to run the server and point it at your sensor.
