Author Topic: How to upload from Ambient to CWOP for free using any computer without weewx  (Read 826 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: 2219
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 | Ecowitt GW1000 | WeatherBridge (Meteobridge)
WU: KFLWINTE111  |  PWSweather: KFLWINTE111
CWOP: FW3708  |  AWEKAS: 14814
Windy: pws-f075acbe
Weather Underground Issue Tracking
Tele-Pole flag pole not up yet

Offline kbellis

  • Forecaster
  • *****
  • Posts: 357
@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: 2219
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 | Ecowitt GW1000 | WeatherBridge (Meteobridge)
WU: KFLWINTE111  |  PWSweather: KFLWINTE111
CWOP: FW3708  |  AWEKAS: 14814
Windy: pws-f075acbe
Weather Underground Issue Tracking
Tele-Pole flag pole not up 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.

Offline shelumiel

  • Member
  • *
  • Posts: 1
ambient_api and 'ewqcb script' in Windows 10
« Reply #7 on: May 03, 2019, 05:18:56 PM »
I'm a complete beginner in Python and spent many hours to figure out how to make things work using my Synology NAS. I ended up installing Python and ambient_api in Windows 10 as a virtual machine. I just wanted to leave step-by-step instructions for other beginners like me.  ;)

1. Download Python 2.7.16 (Windows x86-64 MSI installer, released on 03/04/19) from https://www.python.org/downloads/release/python-2716/
and install.
  * Don't forget to check "Add python.exe to Path" at the bottom of the feature page (you might need to scroll down).
  ** I found the latest Python 3 (3.7.3) doesn't work for 'ewqcb script.'

2. Get an 'API key' as well as an 'application key' from https://dashboard.ambientweather.net/account
  * You need to create each separately. 

3. Open Notepad, copy and paste the code from ewqcb's original post (STEP FOUR). Insert your information to ???? (6 in total, leave single quotation marks).
  ** I have WS-8478 which doesn't have the solar radiation sensor, thus needed to delete the following section from the script.
   
Code: [Select]
# 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)

also

# 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


4. Save the code as a Python script. (ex. Ambient_APRS.py)
 
5. Open Command Prompt (type 'cmd' in Search Windows)
  a. Type 'pip install ambient_api'
  b. Type 'pip install urllib3==1.24.3'
  ** If you need to uninstall those later, type 'pip uninstall' instead.

6. Now, find out where you saved the script in step 4 above, and type 'python YOUR_FILE_NAME.py' in Command Prompt
  * If correctly installed, you'll get a result like
   
Quote
FW0000>APRS,TCPIP*:@032000z000.00N/00000.0W_315/006g007t067r001p...P001h81b10138WS-0000

7. Open Task Scheduler (type in Search Windows) to automatically repeat the Python script.
   * Watch https://www.youtube.com/watch?v=n2Cr_YRQk7o to get help.

8. Finally, check your uploaded data in http://www.findu.com/cgi-bin/raw.cgi?call=FW0000


***** [Added] While the data seems to feed into CWOP smoothly with this script, it turned out coordinates are still being reported in wrong digits (fewer zeros), as ewqcb mentioned in his original post.
Quote
Failed to parse message 1
This report was not parsed as a valid APRS message: Invalid uncompressed location.
Quote
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.
I wonder whether it's because the original Python module (ambient_api) got fixed after ewqcb's script, or due to another new issue. I ended up directly inserting correct coordinates as CWOP requires, disabling the entire converting section... ewqcb, could you please check whether your script works with the current ambient_api module?

(P.S. to ewqcb: It seems Python 2.7 will be discontinued in January next year. Would it be possible for you to update your original script so that it can work in Python 3 or 2.8(?) in the future? Thank you so much for helping us!)  [tup]
« Last Edit: May 03, 2019, 08:46:00 PM by shelumiel »