Weather Related Organizations > CWOP Forum

Hook up your Ambient WS-2902A to APRS using serverless computing, free

(1/2) > >>

k9mjm:
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.

galfert:
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)

k9mjm:
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.

k9mjm:
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.

N8RJC:
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

Navigation

[0] Message Index

[#] Next page

Go to full version