Author Topic: How to upload from Ambient to CWOP for free using any computer without weewx  (Read 425 times)

0 Members and 1 Guest are viewing this topic.

Offline ewqcb

  • Member
  • *
  • Posts: 19
After much trial and error, here’s my method for uploading to CWOP without any special equipment and for free. All you need is a computer that’s always on. You can use a Raspberry Pi, remote server, etc. The script pulls data from the Ambient API, processes it into an APRS packet, and uploads the packet—this avoids mucking around with intercepting traffic on the local network, and has the added benefit of making it so the computer doesn’t have to be anywhere near the station itself.

This assumes you’re already registered through CWOP and have a callsign.

The script uses the ambient_api library by avryhof (https://github.com/avryhof/ambient_api). He also makes a library specifically for uploading to APRS (https://github.com/avryhof/ambient_aprs), but at the time I was trying to figure this out, I noticed it had a bug that cause coordinates to be reported in the wrong format. He has since corrected it, but by then I already had my own simplified script, which streamlines some stuff and has the added benefit of reporting zeroes when the values are actually zero, instead of omitting the data entirely. That’s a style preference, so please check out his work if you’d rather.

STEP ONE: install Python on whatever computer you’re going to be using. I suggest lots of Googling if you need instructions on how, that’s the only way I figure anything coding-related out. My version is 2.7.10; can’t guarantee any other versions.

STEP TWO: install ambient_api per the instructions on his GitHub page, or whatever other method you prefer if you’re more tech savvy.

STEP THREE: obtain Ambient API keys. Do this by emailing support@ambientweather.com and providing your Ambient device’s MAC address. You’ll get back both an application key and an API key.

STEP FOUR: copy and paste the following code into a file with a “.py” extension. You can name it whatever you want and put it anywhere you want. Edit the required values, marked by ????, in the code to customize it to your station. There should be 6 values to customize (API key, application key, callsign, latitude, longitude, and device name), so make sure you get them all.
Code: [Select]
import os
from socket import *
from datetime import datetime, time
import math

os.environ["AMBIENT_ENDPOINT"] = 'https://api.ambientweather.net/v1'
os.environ["AMBIENT_API_KEY"] = '????'
os.environ["AMBIENT_APPLICATION_KEY"] = '????'

from ambient_api.ambientapi import AmbientAPI

callsign = '????'
latitude = ????
longitude = ????
devicename = '????' #This identifies your equipment/software. You can put anything you want. I use 'WS2902A', which is the model of weather station I have
#IMPORTANT: lat/long must be listed in DECIMAL DEGREES (DD.DDDD). Number of digits doesn't really matter. Use positive values for N/E, negative for S/W. The program then converts to degrees decimal minutes (DD MM.MMMM), which is the format APRS requires.

api = AmbientAPI()

devices = api.get_devices()
home = devices[0] #this assumes you have only one station. Increase number accordingly if you want to get data from others
weather= home.last_data

#convert coordinates to degrees decimal minutes
if latitude < 0:
    latitude = abs(latitude)
    latitude = str(int(latitude)).zfill(2) + str(round(60*(latitude - int(latitude)),2)).zfill(2) + 'S'
else:
    latitude = str(int(latitude)).zfill(2) + str(round(60*(latitude - int(latitude)),2)).zfill(2) + 'N'

if longitude < 0:
    longitude = abs(longitude)
    longitude = str(int(longitude)).zfill(3) + str(round(60*(longitude - int(longitude)),2)).zfill(2) + 'W'
else:
    longitude = str(int(longitude)).zfill(3) + str(round(60*(longitude - int(longitude)),2)).zfill(2) + 'E'

winddir = str(weather.get('winddir')).zfill(3)
windspeed = str(int(math.ceil(weather.get('windspeedmph')))).zfill(3)
windgust = str(int(math.ceil(weather.get('windgustmph')))).zfill(3)
if weather.get('tempf') < 0:
    temp = '-' + str(int(round(weather.get('tempf')))).zfill(2)
else:
    temp = str(int(round(weather.get('tempf')))).zfill(3)
rainhour = str(int(weather.get('hourlyrainin')*100)).zfill(3) #technically this is RATE of rain per hour, not AMOUNT per hour, but seems to be tolerated?
past24hoursrain = str(int(weather.get('dailyrainin')*100)).zfill(3) #at the moment, the Ambient API does not provide "rain in last hour", so no way to calculate "rain in last 24 hours." The API can only report "rain since local midnight." Therefore this only gets reported after 23:45 local time, so rain since midnight is reasonably close to rain in last 24 hours
dailyrain = str(int(weather.get('dailyrainin')*100)).zfill(3) #this value IS supposed to be "rain since local midnight," so it is always reported
pressure = str(int(weather.get('baromrelin')/0.0029529983071445)).zfill(5) #pressure is supposed to be reported to APRS in "altimiter" (QNH) format, that is, relative. The system itself corrects the pressure to sea level based on your station's listed elevation, so make sure that's accurate
humidity = str(int(weather.get('humidity')%100)).zfill(2) #uses modulus operator % so that 100% is given as '00'

# If luminosity is above 999 W/m^2, APRS wants a lowercase L
if weather.get('solarradiation') >= 1000:
luminosity = 'l' + str(int(round(weather.get('solarradiation'))) % 1000).zfill(3)
else:
luminosity = 'L' + str(int(round(weather.get('solarradiation')))).zfill(3)

# Time reported in Zulu (UTC). 24-hour rain workaround still has to be local time, though
packet = callsign + '>APRS,TCPIP*:@' + datetime.utcnow().strftime("%d%H%M") + 'z' + latitude + '/' + longitude + '_' + winddir + '/' + windspeed + 'g' + windgust + 't' + temp + 'r' + rainhour + 'p' + (past24hoursrain if datetime.now().time() >= time(23,45) else '...') + 'P' + dailyrain + 'h' + humidity + 'b' + pressure + luminosity + devicename

print(packet) #prints the assembled packet for debugging purposes

#send the packet
s = socket(AF_INET, SOCK_STREAM)
s.connect(('cwop.aprs.net', 14580))
s.send('user ' + callsign + ' pass -1 vers Python\n')
s.send(packet+'\n')
s.shutdown(0)
s.close()

STEP FIVE: run the script once by executing ‘python [your script]’ in a terminal window. It should spit out a correctly formatted APRS packet and upload it to CWOP successfully.

STEP SIX: set a CRON job to run that script automatically at an interval of your choosing (no more than 5 minutes to be polite to the APRS servers, every 10 minutes seems to be the norm). Google how to do this on your specific device. I upload every 5 minutes so the CRON task I use is as follows:
Code: [Select]
*/5 * * * * python [path to your script]
And that should do it. Check findu to make sure your packets are coming through, and make sure your computer never goes to sleep.

Good luck!! Let me know if any of that isn’t working and I’ll do my best to help out, but for almost anything device-specific, Google is going to be smarter and faster than me.
« Last Edit: February 17, 2019, 11:53:14 AM by ewqcb »

Offline galfert

  • Forecaster
  • *****
  • Posts: 1767
Re: How to upload from Ambient to CWOP for free using any computer
« Reply #1 on: February 16, 2019, 10:44:10 PM »
Most impressive. Thank you for sharing.

Although there are other methods to get data from an Ambient station there are some drawbacks to some of the other methods. Most notably using the WeeWx with Interceptor driver requires that your station console be on a separate WiFi network on the Raspberry Pi for the Interceptor driver to be able to work and do it's thing. It is nice to see the Ambient API being used in such a clever way.
WS-2000 & WS-2902A | ObserverIP | WeatherBridge (Meteobridge) | Ecowitt GW1000
WU: KFLWINTE111  |  PWSweather: KFLWINTE111
CWOP: FW3708  |  AWEKAS: 14814
Windy: pws-f075acbe
Tele-Pole flag pole is here (not installed yet)

Offline kbellis

  • Forecaster
  • *****
  • Posts: 330
@Adam - Yes! This is much appreciated!! And many thanks also, to Amos Vryhof for his great work.

The fact that this procedure (so-called Method 5) allows for data to flow to CWOP without weewx is good news for a couple of reasons, I think, though the full machinations in either weewx or Method 5's delivery I've yet to plumb. The first reason: there is nothing wee about weewx. It's a massive, mature and complex set of algorithms lacking any graphical user interface; friendly or otherwise, run from a daemon or terminal in UNIX-speak, maybe not every Ambient customer's area of interest. The second reason this method is good news, or might be good news, relates to what ends up getting done to the original station's data in order to placate Automatic Packet Reporting System (APRS) formatting requirements; more specifically: pressure, barometer, and altimeter. Having an independent producer of these pressure values might (hopefully) be good to use as a check.


Offline mountainbillies

  • Member
  • *
  • Posts: 1
Hi,

I am trying to get the python script working (using Python 3.7) to send the ambient 2902 data to CWOP and followed the instructions in your post.  I got the AMBIENT api key and  application keys from my account on ambient. I get the following error:    Thanks Paul


c:\Data\Paul\AmbientWxStation>PostAmbientToCWOP.py
Traceback (most recent call last):
  File "C:\Data\Paul\AmbientWxStation\PostAmbientToCWOP.py", line 21, in <module>
    home = devices[0] #this assumes you have only one station. Increase number accordingly if you want to get data from others
IndexError: list index out of range

Offline ewqcb

  • Member
  • *
  • Posts: 19
I am trying to get the python script working (using Python 3.7) to send the ambient 2902 data to CWOP and followed the instructions in your post.  I got the AMBIENT api key and  application keys from my account on ambient. I get the following error:    Thanks Paul


c:\Data\Paul\AmbientWxStation>PostAmbientToCWOP.py
Traceback (most recent call last):
  File "C:\Data\Paul\AmbientWxStation\PostAmbientToCWOP.py", line 21, in <module>
    home = devices[0] #this assumes you have only one station. Increase number accordingly if you want to get data from others
IndexError: list index out of range

If I had to guess, I'd say the script didn't successfully connect to the Ambient API, and it's saying there isn't any data (that's why it's saying 0 is out of range--if devices[] has any data, it would start with index 0). I would recommend double-checking your API credentials and trying again. Otherwise, try PMing me the script itself and I can take a look?

Offline galfert

  • Forecaster
  • *****
  • Posts: 1767
I noticed that mountainbillies is using Windows and not Linux. Is your Python script compatible with Windows? He is also writing to a directory off the root of the C: drive which which also requires proper permissions for programs to access this location.
« Last Edit: April 15, 2019, 10:55:32 AM by galfert »
WS-2000 & WS-2902A | ObserverIP | WeatherBridge (Meteobridge) | Ecowitt GW1000
WU: KFLWINTE111  |  PWSweather: KFLWINTE111
CWOP: FW3708  |  AWEKAS: 14814
Windy: pws-f075acbe
Tele-Pole flag pole is here (not installed yet)

Offline ewqcb

  • Member
  • *
  • Posts: 19
I noticed that mountainbillies is using Windows and not Linux. Is your Python script compatible with Windows? He is also writing to the root of the C: drive which which also requires proper permissions for programs to access this location.

Python is compatible with all operating systems, so the script should work anywhere there is a proper Python installation. We know Python is working, since it's throwing error messages.

Quote
I am trying to get the python script working (using Python 3.7) to send the ambient 2902 data to CWOP and followed the instructions in your post.

Didn't notice before that you are using Python 3.7, however. Like I said in the post, I can't guarantee any other versions of Python, particularly Python 3, which is quite a bit different in many regards--I'm on Python 2.7 for compatibility issues. The script may well not work with Python 3.x. You can either try to diagnose the problem yourself, or try to install a different version of Python (a common solution, since many scripts don't work with Python 3.x). I'd recommend Googling to help with that, which is how I figure out anything coding-related anyway.

 

anything