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
- Install and commissioning: siting, calibration and confirming the data is good.
- Your station on WxAlerts: where your observations surface once they are flowing.
- Building one from scratch: if the commercial options do not fit.
- Home Assistant: the other direction, WxAlerts data as entities.
