Community. Driven. Weather. Data. | Always Ad-Free | Developer FriendlyChecking statusDiscord
WxAlerts.org, Community Driven Weather Data
Support us
Sign in
Weather stations

From a station you already own

The fast path, with a Davis, Ecowitt or Ambient station, a Raspberry Pi running WeeWX or CumulusMX, and you are on CWOP in an afternoon.


If you already own a weather station, or you are buying one for a school, a sports facility or an EOC, this is the shortest route onto CWOP. Buy supported hardware, run bridge software, done. No soldering and no protocol work.

The hardware landscape

Davis Vantage Pro2 and Vantage Vue are the de facto standard for serious hobbyist and light professional use. Data comes off through WeatherLink loggers over USB or serial, through WeatherLink Live over Ethernet or WiFi (which exposes a local JSON API as well as the cloud) or through IP loggers. Davis’s own WeatherLink cloud can forward straight to CWOP without any bridge at all.

Ecowitt gateways (GW1000, GW1100, GW2000, GW3000), the WS90 “Wittboy” haptic array and the WH-series sensors are the best value in DIY-friendly commercial gear. The gateways expose a local API and a “custom server” upload you can point at anything you like, which is what makes them so easy to bridge. The WS90 has no moving anemometer parts, so it barely needs maintenance, though verify its haptic rain accuracy against a bucket before trusting it.

Ambient Weather (the WS-2902 and its relatives) is an inexpensive WiFi console that uploads to ambientweather.net, which forwards to Weather Underground and PWSweather. CWOP usually needs an intermediary.

La Crosse and AcuRite are consumer-grade. AcuRite’s 5-in-1 and Access can be received directly with an SDR; see below.

The Raspberry Pi bridge

A Pi, and a Zero 2 W is plenty, running WeeWX or CumulusMX is the standard approach. It polls the station over USB or TCP, or receives the gateway’s custom-server POST, computes derived fields like MSLP and rain rate, and uploads to CWOP every five minutes. Raspberry Pi OS Lite is all you need.

Choosing the software

Software Platform CWOP built in Best for Status
WeeWX Python, Linux/Pi Yes Davis, Ecowitt/Fine Offset, custom; scriptable Actively maintained (5.x)
CumulusMX .NET/Mono, Win/Linux/Pi Yes Ecowitt and Davis, GUI users Actively maintained (4.x)
Weather Display Windows Yes Broad hardware support Maintained, dated UI
WeatherCat macOS Yes Mac households Niche but maintained
wview Linux Historically none Effectively abandoned. Use WeeWX

Take WeeWX if you are comfortable in Linux and CumulusMX if you want a GUI, especially on Ecowitt hardware.

WeeWX

CWOP is built in. In weewx.conf:

[StdRESTful]
    [[CWOP]]
        enable = true
        station = EW9876           # your CWOP ID, or ham callsign
        # passcode is for hams only; CWOP citizens omit it and get -1
        #passcode = 12345
        post_interval = 300        # 5 minutes, the CWOP convention
        server_list = cwop.aprs.net:14580, cwop.aprs.net:23
        log_success = true

Driver selection lives separately, in [Station] and the driver’s own stanza, the Vantage driver for Davis, or interceptor / gw1000 for Ecowitt.

sudo apt install weewx          # or pip, per weewx.com/docs
sudo systemctl enable --now weewx
journalctl -u weewx -f          # watch it post to CWOP

CumulusMX

Everything is in the GUI, under Settings → Internet → APRS/CWOP. Enter your CWOP ID, leave Pass as -1, set Server to cwop.aprs.net, Port to 14580 and Interval to 5 minutes. Tick Include Solar rad only if you actually have a pyranometer. Then tick Enabled. Latitude and longitude come from Station Settings automatically.

For Ecowitt into CumulusMX, set station type to HTTP (Ecowitt), then on the gateway configure a Customized weather server: protocol Ecowitt, IP the Pi’s address, port 8998 or whatever you gave CumulusMX, path /station/ecowitt, interval 20 s. The GW1x00 and GW2000 also support a local API mode CumulusMX can poll directly.

Ecowitt custom server, straight to your own code

If you would rather not run a full weather program, the gateway will POST to any endpoint you name. Set Weather Services → Customized → Protocol: Ecowitt, server IP your Pi, path /data/report/, port 8080, interval 60 s.

A small HTTP listener then parses the form-encoded payload (tempf, humidity, windspeedmph, windgustmph, winddir, baromrelin, hourlyrainin, dailyrainin) and builds the APRS packet. WeeWX’s interceptor driver does exactly this if you would rather not write it yourself.

A minimal uploader

Dependency-free, and short enough to read in one sitting. This implements the handshake and field encoding from the packet format page.

#!/usr/bin/env python3
"""Minimal CWOP uploader. Sends ONE weather packet."""
import socket, time, sys

CALL   = "EW9876"           # CWOP ID or ham callsign
PASS   = "-1"               # "-1" for CWOP; computed passcode for hams
SOFT   = "WxAlertsPy 1.0"   # software name (no spaces) + version
SERVER = ("cwop.aprs.net", 14580)
LAT    = "3316.04N"         # ddmm.hhN, leading zeros, exactly 2 decimals
LON    = "09631.96W"        # dddmm.hhW

def f3(v):                  # 3 digits, zero-padded; dots when unknown
    return "..." if v is None else f"{int(round(v)):03d}"

def wx_packet(wdir, wspd_mph, gust_mph, temp_f, rain1h_100, rain24_100,
              rainmid_100, rh, mslp_hpa):
    ts = time.strftime("%d%H%M", time.gmtime()) + "z"
    h  = 0 if rh >= 100 else int(round(rh))        # h00 means 100%
    b  = f"{int(round(mslp_hpa * 10)):05d}"        # tenths of hPa
    body = (f"@{ts}{LAT}/{LON}_"
            f"{f3(wdir)}/{f3(wspd_mph)}g{f3(gust_mph)}t{f3(temp_f)}"
            f"r{f3(rain1h_100)}p{f3(rain24_100)}P{f3(rainmid_100)}"
            f"h{h:02d}b{b}.WxAl")                   # .WxAl = equipment tag
    return f"{CALL}>APRS,TCPIP*:{body}"

def send(pkt):
    with socket.create_connection(SERVER, timeout=15) as s:
        s.settimeout(15)
        s.recv(512)                                 # banner
        s.sendall(f"user {CALL} pass {PASS} vers {SOFT}\r\n".encode())
        s.recv(512)                                 # login ack
        s.sendall((pkt + "\r\n").encode())
        time.sleep(2)                               # let it flush
    print("sent:", pkt)

if __name__ == "__main__":
    try:
        send(wx_packet(wdir=120, wspd_mph=5, gust_mph=10, temp_f=21,
                       rain1h_100=0, rain24_100=0, rainmid_100=0,
                       rh=75, mslp_hpa=1032.2))
    except (socket.timeout, OSError) as e:
        print("upload failed:", e, file=sys.stderr)
        sys.exit(1)

Run it from a five-minute cron job or systemd timer. Remember that b carries MSLP in tenths of a hectopascal, and that h00 means 100% humidity.

No vendor gateway at all: rtl_433

A roughly $30 RTL-SDR dongle can receive most 433 and 915 MHz stations directly and republish them to MQTT, cutting the vendor console out entirely.

# AcuRite 5-in-1 (protocol 40) -> MQTT, one topic per model/id
rtl_433 -f 915M -R 40 \
  -F "mqtt://192.168.1.10:1883,retain=0,devices=rtl_433[/model][/id]"

# Generic 433 MHz station, SI units, UTC timestamps
rtl_433 -f 433.92M -C si -M utc -F json \
  | mosquitto_pub -t home/rtl_433 -l -h 192.168.1.10

Topics come out like rtl_433/Acurite-5n1/<id>/wind_avg_mi_h, and .../wind_dir_deg, .../temperature_F, .../humidity, .../rain_in alongside. A small paho-mqtt subscriber then feeds the uploader above.

Home Assistant as the intermediary

If Home Assistant is already your hub, it can ingest those MQTT topics as mqtt: sensors with a value_template, handle unit conversion and averaging, and push to CWOP through a script, AppDaemon, or a community APRS integration. It is a reasonable choice when HA is already running. If it is not, WeeWX is the more direct path.

Where to go next