Author Topic: Hook up your Ambient WS-2902A to APRS using serverless computing, free  (Read 359 times)

0 Members and 1 Guest are viewing this topic.

Offline k9mjm

  • Member
  • *
  • Posts: 4
I was dismayed to find out that my new Ambient WS-2902A didn't have the capability of sending data directly to APRS. There were other posts describing methods for getting that data out of Ambient's REST API and into APRS, but all of them involved doing something like custom router firmware or running a script on a computer (single point of failure)...

I thought this was an excellent use for AWS Lambda. This lets you run your code in a high-availability environment, for free. You don't even have to pay for the electricity this runs on. Jeff Bezos has it covered. Steps to do it:

  • Get an amateur radio license ;)
  • Set up your station to report to AmbientWeather.net through the awnet phone app.
  • Create an Application Key and API Key at https://dashboard.ambientweather.net/account.
  • Create yourself an account at Amazon and log in to Amazon Web Services.
  • Go to the AWS Lambda service. https://console.aws.amazon.com/lambda/home?region=us-east-1#/create/function
  • Click "Create function", and choose the "Author from scratch" method. I named my function ambientToAPRS. Make sure to choose the Python 3.8 runtime.
  • Replace the sample code with this (and replace the values of callAndSSID, aprsPasscode, latitude, longitude, apiKey, mac, and applicationKey - no, those are not really mine). The APRS specification designates SKY as a destination address for SKYWARN stations. In my case, that is what I used. Looks like everyone else just uses APRS as the destination.

    import urllib, json, time, http.client
    from urllib.request import urlopen

    def lambda_handler(event, context):
        # REPLACE THESE VALUES ########
        mac = 'DC:4F:22:59:13:12'
        apiKey = '438955956ece44d9a2984ae15e46fb162303311747ef4390b930dee08d6accea'
        applicationKey = 'd984259457f445d6a6c2bda2896ca23e0430163bb8be4a49b424b86d8072779f'
        # Your call and substation ID go here, e.g. KN4DVB-1
        callAndSSID = 'KN4DVB-1'
        # Your APRS passcode - https://apps.magicbug.co.uk/passcode/
        aprsPasscode = str('124324')
        # latitude must be exactly 7 chars - include leading zeros
        latitude = '3612.70N'
        # longitude must be exactly 9 chars - include leading zero(s)
        longitude = '08141.69W'
        destination = 'APRS'
        #uncomment (remove the # from) the following line if you are a real, trained NWS SKYWARN watcher
        #destination = 'SKY'
        #########################
        url = 'https://api.ambientweather.net/v1/devices/' + mac + '?apiKey=' + apiKey + '&applicationKey=' + '&endDate=&limit=1'
        json_url = urlopen(url)
        data = json.loads(json_url.read())
        diff = time.time() - (data[0]["dateutc"]/1000)
        responsebody = ""
        if (diff < 800):
            postbody = 'user ' + callAndSSID +' pass ' + aprsPasscode +'\n'
            dhmtime = str(data[0]["date"][8:10]) + str(data[0]["date"][11:13]) + str(data[0]["date"][14:16]) + "z"
            postbody += callAndSSID + '>' + destination + ',WIDE1-1:@' + dhmtime + latitude + '/' + longitude + '_'
            # wind direction
            postbody += 'c'
            postbody += (str('000') + str(data[0]["winddir"]))[-3:]
            # wind speed
            postbody += 's'
            postbody += (str('000') + str(round(float(data[0]["windspeedmph"]))))[-3:]
            # wind gust
            postbody += 'g'
            postbody += (str('000') + str(round(float(data[0]["windgustmph"]))))[-3:]
            # temp F
            postbody += 't'
            tempf = int(data[0]["tempf"])
            if (tempf < 0):
                postbody += "-" + (str("00") + str(tempf))[-2:]
            else:
                postbody += (str("000")+str(tempf))[-3:]
            # rain in last hour
            postbody += 'r'
            postbody += (str('000') + str(int(data[0]["hourlyrainin"]*100)))[-3:]
            # rain in last day
            postbody += 'P'
            postbody += (str('000') + str(int(data[0]["dailyrainin"]*100)))[-3:]
            #humidity
            postbody += "h"
            postbody += (str('00') + str(data[0]["humidity"]))[-2:]
            #pressure
            postbody += "b"
            postbody += (str('00000') + str(int(10*33.864*data[0]["baromrelin"])))[-5:]
            posturl = "http://rotate.aprs2.net:8080/"
            conn = http.client.HTTPConnection('rotate.aprs2.net',8080)
            headers = {'Content-Type':'application/octet-stream','Accept-Type':'text/plain'}
            conn.request('POST', '/', postbody, headers)
            responsebody = str(conn.getresponse().read())
        return {
            'statusCode': 200,
            'body': responsebody
        }
  • Save your function and test it... you should see your beautiful WX icon show up at https://aprs.fi .
  • Go to CloudWatch Rules: https://console.aws.amazon.com/cloudwatch/home?region=us-east-1#rules:
  • Create a rule. I called mine ambientToAPRS-runner.
  • In the Event Source, set it to Schedule, with fixed rate of 1 minute.
  • Click "Add Target", and choose your ambientToAPRS lambda function.
  • Click "Configure Details".
  • Put in the name of your rule, and set state to Enabled.
  • Click "Create Rule".
  • Go back to aprs.fi in 10 minutes, and pull up some beautiful weather graphs for your station.

The AWS Lambda free usage tier includes 1M free requests per month and 400,000 GB-seconds of compute time per month. You will run 44,640 requests in a 31-day month (44,641 if they ever add a leap second in a 31-day month).

If you want the data copied to CWOP, my understanding is that you have to register your callsign with CWOP (I'll get on this today).

My next project: Decode the 915 MHz signal from the sensor array and push it to a local iGate on RF APRS on a solar-powered Arduino with an SDR receiver and a UV-5R transmitter.

« Last Edit: February 23, 2020, 06:48:03 PM by k9mjm »

Offline galfert

  • Global Moderator
  • Forecaster
  • *****
  • Posts: 4677
Re: Hook up your Ambient WS-2902A to APRS using serverless computing, free
« Reply #1 on: February 18, 2020, 10:55:59 AM »
Very nice!

Users should note that if they don't have a ham call sign and are using a regular CWOP ID that they should change the upload server to cwop.aprs.net instead of rotate.aprs2.net (and then APRS passcode is -1)
Ecowitt GW1000 | Meteobridge (WeatherBridge)
WU: KFLWINTE111  |  PWSweather: KFLWINTE111
CWOP: FW3708  |  AWEKAS: 14814
Windy: pws-f075acbe
Weather Underground Issue Tracking
Tele-Pole

Offline k9mjm

  • Member
  • *
  • Posts: 4
Re: Hook up your Ambient WS-2902A to APRS using serverless computing, free
« Reply #2 on: February 18, 2020, 05:40:47 PM »
I should mention that the Lambda environment does not seem to do any DNS caching... each sequential packet is routing through a random server through the load-balancing provided by rotate.aprs2.net. Both APRS and AWS provide you with some level of high availability. I don't know how HA the AmbientWeather.net API is, but they do allow you to pull more than one observation.

I could make this account for downtime on APRS and on AmbientWeather.net by tracking the last successful push to APRS and then pushing all available observations since that timestamp from AmbientWeather.net, since they let you pull the last 200+ observations. I'll worry about that when I see downtime in excess of 10 minutes.

Offline k9mjm

  • Member
  • *
  • Posts: 4
Re: Hook up your Ambient WS-2902A to APRS using serverless computing, free
« Reply #3 on: February 24, 2020, 01:54:43 PM »
Anyone using the above code, please note that I modified it this weekend to round windspeeds to the nearest integer mph. APRS does not accept fractional windspeeds.

Offline N8RJC

  • Member
  • *
  • Posts: 1
Thank you for putting this together!  I just set mine up in AWS this morning and it worked beautifully.

One thing I noticed, is that it expects Latitude and Longitude as DMS formatted as DDMM.SS, whereas I assumed it was all decimal.  Going to have some bad position history on aprs.fi for a couple days  #-o

Offline galfert

  • Global Moderator
  • Forecaster
  • *****
  • Posts: 4677
It is actually DDMM.mm

This online converter should help
https://www.directionsmag.com/site/latlong-converter/


Ecowitt GW1000 | Meteobridge (WeatherBridge)
WU: KFLWINTE111  |  PWSweather: KFLWINTE111
CWOP: FW3708  |  AWEKAS: 14814
Windy: pws-f075acbe
Weather Underground Issue Tracking
Tele-Pole

 

anything