Sunday, December 29, 2019

Weather sonde automated prediction

Weather Sonde automated prediction


So, I've gotten the bug to chase weather balloons.  The balloons in my region are launched by the National Weather Service from a location adjoining the airport in Buffalo, New York.   Only a small percentage of the balloons make it southeast enough to be within reasonable chase distance from my home near Ithaca, New York, 110 air miles away.  I ran manual predictions every day, to see how the balloons might progress, but that got tedious.  I decided some automation was in order.

CUFS Prediction Software

To begin, I picked up the CUFS Prediction Wrapper for python.  It provided tools to download the latest NWS prediction data as well as tools to run predictions.  I run a Raspberry Pi at home, so I set up cron jobs to load the data each morning, and to run predictions for the next 7 days.

Downloading Wind Data

I set up a cron job to download the wind data at 3am each day:

0 3 * * * /home/mqh1/bin/wind_grabber.sh > /home/mqh1/balloons/cusf/logs/windgrabber.log 2>& 1

The contents of the "wind_grabber.sh' script are as follows:

python \
/home/mqh1/balloons/cusf/cusf_predictor_wrapper/apps/get_wind_data.py \
--lat=42 --lon=-77 --latdelta=2 --londelta=4 -f 180 \
-m 0p25_1hr -o /tmp/gfs/

The lat/long as well as latedelta/londelta parameters are sufficient to include most of the likely flight locations of a weather balloon out of Buffalo.  I cache the results in /tmp/gfs on the pi.  "-f 180" is looking for the next 180 hours of predictions (7 1/2 days).

The result is that I have current wind data automatically downloaded on a daily basis that should cover the areas of interest for any NWS flights.  Since I do my own High Altitude Balloon launches, as well as pico flights, I don't feel too guilty about downloading this data daily.

Todo: I'd like to find out when the NWS uploads the data, and tune my download and daily prediction schedule to use the latest data.

Running predictions


Typical flights

I was curious what the best parameters would be to choose for ascent rate / descent rate / and burst altitude for the NWS balloon.  Fortunately, two of the three are provided on a regular basis by the NWS.  They have an index with the codes for all the launch locations. I was able to download a big file with all of the flight data from the last two years of flights.  I stuck it in a spreadsheet, and did some number crunching.  I had to eliminate a few flights that were clearly outliers.  They had really screwy ascent rates or burst altitudes compared to the rest.  I removed the anomalies that had glaring differences, and ran averages on the remainder.  I got the following averages.

Average burst altitude: 31,420 meters
Average ascent rate: 5.28 m/sec

Unfortunately, the NWS doesn't log the descent data, so I don't have a good measure of the average descent rate of the sondes.  Based on conversations with folks who do this, it seems like most people go with 5m/s.  It can vary widely however.

Todo: If I get a fully automated station running, I'd like to get better data about descent rates and decide how to handle it.  I might use an average, or I might do predictions on each end of the range.

Automated predictions

The CUSF software makes it really easy to run predictions.  I run them after I download the GFS data.  I haven't measured the typical download rate yet, so for now, I run them 90 minutes after the  wind data download.

30 4 * * * /home/mqh1/bin/sonde_predict.py > /home/mqh1/balloons/cusf/logs/sonde_predict.log 2>& 1

The prediction script is a bit more complicated than the download script.  I started from the sonde_predict.py script provided by the CUSF wrapper and made three principal mods. 

  1. Determining which predictions are coming in "near' Ithaac
  2. Generating two separate KML files.  One for "nearby" and one for "all" predictions.
  3. Emailing results.

Determining nearby predictions

After the prediction runs, I take the final landing coordinates and determine whether they are "nearby" Ithaca.  I do this with a Point in Polygon (PIP) algorithm.

 I used Google Earth to draw a bounding box covering an area around Ithaca in which I was willing to search for balloons (Add / Polygon).  I then exported the polygon to a KML (right-click, Save Place As),  I was then able to grab the coordinates from the KML file, and just copy/paste them to create an array in the code.  Note, I had edit the data to reverse the lat/long coming out of the KML file to make them more friendly.  In retrospect, I could have left them reversed, and just reversed the order in the code below.  Still, I think this is more "human friendly" to see them in the typical order.

# Coordinate around Ithaca (larger grid)
nearby_coords = [
   (42.5894692373013, -77.13750089643533),
   (42.13873798563936, -77.11683620792473),
   (42.1355764571338, -75.91998110781731),
   (42.62563890419801, -75.89559117314741)
   ]


The coords of the prediction of the payload are in the script in the flight_path[] array, so we grab the last position.

        finalcoords = flight_path[-1]
        latlong = (finalcoords[1], finalcoords[2])
        landingsite = Point(latlong)

Then we can determine whether it's in the "nearby_coords" polygon we've established:

        if landingsite.within(nearby_poly):

Creating a second KML file


If we're within the "nearby_poly", I add the KML plot to a separate list.  I modified the provided script to generate two KML files.  One for "all' predictions, and one for "nearby" predictions.   I write both of the predictions into an HTML directory for the web server I run on the Pi.  That makes it trivial to download them to my laptop to inspect them with Google Earth.

Emailing results

Finally, I modified the prediction script to generate an email summarizing the number of nearby predictions, and containing HTML links to both the nearby and full KML files.  Each morning, I receive an email that tells me if I have nearby predictions.  If I do, I can click the link to see the predictions for the coming week that land near Ithaca.


In the image below, I highlighted the bounding box for my "nearby" predictions.



Todo: I'd like to find a way to provide a link into google maps which will show the 2D representation of the prediction by just clicking a link, rather than having to download a KML file and launch Google Earth.

Sample Code


Disclaimers: Shipped by weight not volume.  Some settling may occur during shipping.  Not actual size.  Consult your computer professional before running.  If this code causes and erection lasting more than 4 hours, consult a physician.. no, seriously!

#!/usr/bin/env python
#
#   Project Horus
#   CUSF Standalone Predictor Python Wrapper
#       Radiosonde Launch Predictor
#   Copyright 2017 Mark Jessop <vk5qi@rfhead.net>
#
#       In this example we run predictions for a radiosonde launch from Adelaide Airport
#       for every launch for the next 7 days, and write the tracks out to a KML file.
#
#       For this script to work correctly, we need a weeks worth of GFS data available within the gfs directory.
#       This can be gathered using get_wind_data.py (or scripted with wind_grabber.sh), using :
#           python get_wind_data.py --lat=-33 --lon=139 --latdelta=10 --londelta=10 -f 168 -m 0p25_1hr -o gfs
#       with lat/lon parameters chaged as appropriate.
#
# Mods to this script written by Mike Hojnowski / KD2EAT.  It is provided for informational purposes.  It will
# not run in a generic environment without modification.  In particular, look for the email addresses and for
# the home directories sprinkled in paths all over.
#

import smtplib
import time
import fastkml
import datetime
from dateutil.parser import parse
from cusfpredict.predict import Predictor
from pygeoif.geometry import Point, LineString
from cusfpredict.utils import *
from shapely.geometry import Point, Polygon

#Email Variables
SMTP_SERVER = 'smtp.gmail.com' #Email Server (don't change!)
SMTP_PORT = 587 #Server Port (don't change!)
GMAIL_USERNAME = 'somebody@gmail.com' #change this to match your gmail account
GMAIL_PASSWORD = 'ImDumbButNotTHATDumb'  #change this to match your gmail password
SENDTO = 'ThisCouldBeYou@gmail.com'     #change to the recipient of the emails

## Coordinate around Ithaca (smaller grid)
#nearby_coords = [
#   (42.44387782645627, -77.01612142816262),
#   (42.2569086411454,-76.99969683890008),
#   (42.3432446683892, -76.17009222210787),
#   (42.57580936472674, -76.28671583838107),
#   ]
#

# Coordinate around Ithaca (larger grid)
nearby_coords = [
   (42.5894692373013, -77.13750089643533),
   (42.13873798563936, -77.11683620792473),
   (42.1355764571338, -75.91998110781731),
   (42.62563890419801, -75.89559117314741)
   ]

nearby_poly = Polygon(nearby_coords)

# BIG polygon for testing. No predictions were near Ithaca the day I was coding, so I made a bigger polygon.
huge_coords = [
   (43.44387782645627, -78.01612142816262),
   (41.2569086411454,-78.99969683890008),
   (41.3432446683892, -75.17009222210787),
   (43.57580936472674, -75.28671583838107),
   ]

# Just substitude "huge_poly" for "nearby_poly" in the code below, if you need to test during unfavorable predictions.
huge_poly = Polygon(huge_coords)


# Predictor Parameters
PRED_BINARY = "/home/mqh1/balloons/cusf/cusf_predictor_wrapper/apps/pred"
GFS_PATH = "/tmp/gfs"

NEARBY_OUTPUT_FILE = "/home/mqh1/public_html/nearby_predictions.kml"
ALL_OUTPUT_FILE = "/home/mqh1/public_html/all_predictions.kml"

# Launch Parameters - Update as appropriate for your launch site
# Launch parameters based on NWS data from 01/01/2018 - 12/22/2019 averages
LAUNCH_LAT = 42.9414
LAUNCH_LON = -78.7193
LAUNCH_ALT = (218+500)/2.0;             # Barometric and GPS varied.  I took the average.
ASCENT_RATE = 5.26
DESCENT_RATE = 5.0
BURST_ALT = 31420

# Set the launch time to the current UTC day, but set the hours to the 12Z sonde
current_day = datetime.datetime.utcnow()
LAUNCH_TIME = datetime.datetime(current_day.year, current_day.month, current_day.day, 11, 15)


# Parameter Variations
# These can all be left at zero, or you can add a range of delta values
launch_time_variations = range(0,168,12) # Every 12 hours from now until 7 days time.

# A list to store prediction results
nearby_predictions = []
all_predictions = []
nearby = 0

# Create the predictor object.
pred = Predictor(bin_path=PRED_BINARY, gfs_path=GFS_PATH)

# Iterate through the range of launch times set above
for _delta_time in launch_time_variations:

        # Calculate the launch time for the current prediction.
        _launch_time = LAUNCH_TIME + datetime.timedelta(seconds=_delta_time*3600)

        print ('Running prediction for ' + str(_launch_time))
        # Run the prediction
        flight_path = pred.predict(
                launch_lat=LAUNCH_LAT,
                launch_lon=LAUNCH_LON,
                launch_alt=LAUNCH_ALT,
                ascent_rate=ASCENT_RATE,
                descent_rate=DESCENT_RATE,
                burst_alt=BURST_ALT,
                launch_time=_launch_time)

        # If we only get a single entry in the output array, it means we don't have any wind data for this time
        # Continue on to the next launch time.
        if len(flight_path) == 1:
                continue

        #print(flight_path[-1])
        coords = flight_path[-1]
        latlong = (coords[1], coords[2])
        #print latlong
        landingsite = Point(latlong)
        #print landingsite.within(nearby_poly)
        #print landingsite.within(huge_poly)

        # Generate a descriptive comment for the track and placemark.
        pred_time_string = _launch_time.strftime("%Y%m%d-%H%M")
        pred_comment = "%s %.1f/%.1f/%.1f" % (pred_time_string, ASCENT_RATE, BURST_ALT, DESCENT_RATE)

        # Add the track and placemark to our list of predictions
        all_predictions.append(flight_path_to_geometry(flight_path, comment=pred_comment))
        all_predictions.append(flight_path_landing_placemark(flight_path, comment=pred_comment))

        if landingsite.within(nearby_poly):
                # If nearby, add the placemark to our nearby predictions list
                nearby_predictions.append(flight_path_to_geometry(flight_path, comment=pred_comment))
                nearby_predictions.append(flight_path_landing_placemark(flight_path, comment=pred_comment))
                print("Prediction Succeeds: %s" % pred_comment)
                nearby += 1

# Write out the prediction data to the KML file
kml_comment = "Sonde Predictions - %s" % gfs_model_age()
write_flight_path_kml(all_predictions, filename=ALL_OUTPUT_FILE, comment=kml_comment)
write_flight_path_kml(nearby_predictions, filename=NEARBY_OUTPUT_FILE, comment=kml_comment)

###################
# Email portion

class Emailer:
    def sendmail(self, recipient, subject, content):

        #Create Headers
        headers = ["From: " + GMAIL_USERNAME, "Subject: " + subject, "To: " + recipient,
                   "MIME-Version: 1.0", "Content-Type: text/html"]
        headers = "\r\n".join(headers)

        #Connect to Gmail Server
        session = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
        session.ehlo()
        session.starttls()
        session.ehlo()

        #Login to Gmail
        session.login(GMAIL_USERNAME, GMAIL_PASSWORD)

        #Send Email & Exit
        session.sendmail(GMAIL_USERNAME, recipient, headers + "\r\n\r\n" + content)
        session.quit

sender = Emailer()

sendTo = SENDTO
emailSubject = "Sonde predictions nearby: " + str(nearby)
emailContent = str(nearby) + ' predictions have been detected near Ithaca.<br><br>'  +\
   '\nNearby Predictions: <a href=http://thehojos.com/~mqh1/nearby_predictions.kml>http://thehojos.com/~mqh1/nearby_predictions.kml</a><br><br>' +\
   '\nAll Predictions: <a href=http://thehojos.com/~mqh1/all_predictions.kml>http://thehojos.com/~mqh1/all_predictions.kml</a>'

sender.sendmail(sendTo, emailSubject, emailContent)
print("Email Sent")

Friday, February 22, 2019

DTMF Board with BU8872

Objective

I wanted to create an Arduino shield with a BU8872 DTMF chip on it so that I can decode DTMF from an audio source with an Arduino sketch.  Since the chip itself was only available in a surface-mount SSOP-16 form factor, I elected to do the entire board with surface mount components.

Schematic


I followed the recommended schematic in the datasheet.  I'm routing the audio input into the A5 pin on the arduino header.  This was done just in case I wanted to attempt audio decoding natively on the Arduino.  The three digital pins map to D5,6,7 on the Arduino.  I found a surface-mounted crystal that should work with the chip as well.


Board Layout

The layout was pretty straightforward.  Because I was hand-etching this board, I elected to place components on the back of the board.  This was to ease the soldering of the traces to the header pins.  In a stacking header, the bottom of the shield is the pin side, and the top has the plastic female sockets.  It would be difficult to solder under those plastic sockets to establish the connection, so I put everything on the bottom of the board.

Note, I used a ground plane on this board, but the traces bisected the board.  Though not pictured in this board layout, after assembling the board, I bridged a few traces with 1206 Zero ohm resistors to jumper the ground connection around so that I had a full ground plane.  



Finished board

The finished board is pictured here.  As mentioned above, you can see the two 1206 zero ohm resistors bridging the long traces, assuring that the ground plane has conductivity across the entire board.  In a future revision, I'll formally put pads on the board layout to document their placement.


Test Setup

You can see the board sandwiched on the arduino to the right.  There is a logging shield on top, then the DTMF shield, and finally, an Arduino Leonardo underneath.  I actually tested this using a VHF receiver module, and transmitting from an HT to send the tones. 




Coding challenges

The code for this board is fairly straightforward.  I only ran into one vexing issue  The "DST" pin is supposed to drop cleanly after the DTMF audio tone is dropped.  In practice, there seems to be some "jitter" where the DST pin oscillates between 0 and 1 at the end of the tone.  The datasheet has a little RC filter which might have compensated for that, but I really didn't understand how to calculate appropriate values for the filter components.  In a future board, I'll probably design in the filter, and if I don't want it, I can always jumper it with 0 ohm resistors.  Anyway, the code snip below is very simple, and demonstrates what I had to do to get around the jitter issue on the DST pin.

The Code


#define EST_PIN 5
#define ACK_PIN 6
#define SD_PIN  7
#define EST_JITTER    40         // Number of consecutive reads of EST that must be "off" before assuming tone is over

int ReadDTMF()
{
  int i, dtmf;

  dtmf = 0;
  // Iterate for the 4 bits of data we need to get from the BU8872
  for (i = 0; i <4; i++) {
    digitalWrite(ACK_PIN, 1);               // Pull ACK high
    delayMicroseconds(10);                  // Wait a bit
    dtmf += (digitalRead(SD_PIN) << i);     // Read the value from the SD pin, shifted appropriately and add to the rest
    digitalWrite(ACK_PIN, 0);               // Pull ACK low
    delayMicroseconds(10);                  // Wait a bit 
  }
  return(dtmf);
}

void setup() 
{
  
  Serial.begin(115200);
  while(!Serial);         // Arduino Leonardo issue - wait for the serial port
  Serial.println("Starting");
  Serial.flush();
  pinMode(EST_PIN, INPUT);
  pinMode(ACK_PIN, OUTPUT);
  pinMode(SD_PIN, INPUT);
 // When powered up, the BU8872 is awakened by sending 4 pulses on the ACK pin.  
 // A ReadDTMF will do the trick. We just ignore the result.
  int ignored = ReadDTMF();    
}

void loop() 
{
    int dtmf;
    int i;

    // If we have a DTMF tone, grab it
    if (digitalRead(EST_PIN)) {
       dtmf = ReadDTMF();
       Serial.print("Got DTMF code: ");  Serial.println(dtmf);

      // We have jitter on the EST pin.  Make sure it is clear for several consecutive milliseconds before assuming the tone is over.
      i = 0;
      while (i < EST_JITTER) {
         if (digitalRead(EST_PIN)) {    
            i = 0;    // If the pin jitters high, restart our count
         } else {
            i++;
         }
         delay(1);
      }    
      Serial.println("RST Clear");
    }
}

Sample Output


Future Enhancements

  1. Add the RC filter to the output section.
  2. Test RC filter values to see if the jitter can be eliminated.
  3. Put placeholders in the schematic and board layout for the ground plane jumpers.
  4. Add a trim pot on the audio input line.
  5. Bonus points: Add a test mode to the board and analog pin that puts a peak detector circuit on the pin (or maybe a separate one) and uses the ADC to calculate the RMS voltages of the input audio, to facilitate adjusting the input audio volume.

Bill of Materials

RefID     Digi-Key Description QTY
C1, C2 445-1270-1-ND CAP CER 12PF 50V 5% NP0 0603 2

C3

445-1316-1-ND

CAP CER 0.1uF 25V 10% X7R 0603

1
C5 399-6049-1-ND CAP ALUM 10UF 20% 63V SMD 1
Q1 BU8872FS-E2CT-ND IC DTMF RCVR FOR PHN SSOP-A16 TR 1
Y1 CTX1023CT-ND CRYSTAL 4.194304MHZ 12PF SMD 1

Monday, March 5, 2018

PCB Etching notes

PCB Etching Notes


I did a little research, and found it still took some trial and error to come up with a PCB etching procedure that worked reliably for me.  I also noted that, since I don't etch boards all that often, I kept forgetting how I do it.  So, with that in mind, here are my notes on PCB Etching for my "Note To Self" category.

Materials


Circuit design

  • I use Diptrace to design my PCBs
  • Set the trace width and spacing to 16 mil, if possible.  Thicker traces like this work better for hand etching.  I've succeeded with 10 mil, but the traces were very thin and uneven.  Thicker is better.
  • To put letters on the board (copper characters), use "Objects / Place Text".  Hit Enter to complete text entry.  Then right click on the text.  Under "Properties" set the "Type" to "Signal".  That will leave the letters as copper on the board, and leave some space around it.

Saving Gerbers

  • Nothing special.  Save the Top layer for use in etching.

Print using Gerbv

  • Note, I use Gerbv in Windows.  I don't know how these instructions might work on Linux or Mac systems.
  • By default, gerbv will show the layer in color, rather than in a strong black monochrome.  I set the following properties before printing:
    • Under "Layer / Change Color"
      • Set Opacity slider to 255.
      • Set Color name to "#09090e"
      • The image will turn black on the screen.  
    • Under "Layer / Modify Orientation"
      • Set "Mirroring about Y axis"
      • This adjusts for the fact that we're putting the paper face down, reversing the image on the copper.
  • Do test prints, check that it's very dark, and that the size is right.
  • Laser print on the Gloss paper and cut it out.

Copper Clad preparation

  • Scrub the copper clad with steel wool to get it shiny.
  • Clean it with Isopropyl alcohol.
  • Keep fingers away from the surface.

Toner transfer

  • Use 1 or 2 pieces of scotch tape to adhere the gloss paper, face down, onto the copper clad.
  • Warm up the Laminator on the 5 mil (hotter) setting.
  • Run the copper clad, with paper on upper face, through the laminator about a dozen times.  I rotate the copper clad end to end as I do it.  After about 8 passes, as the copper is getting really hot, I rotate the copper clad 90 degrees and run it through sideways a few times.  When I think it's nearly done, I may do a pass or two with the paper side down.
  • The copper clad should be unpleasantly hot to handle by the time you're done.
  • After about a dozen passes, drop the copper clad into a pan of water.  Keep it submerged for about 5-7 minutes.
  • Remove the copper clad from the water, and peel off the paper.  If all goes well, the paper should pull away leaving the toner on the board.
  • The toner should be well secured to the board.  Go ahead and rub it with your fingertip to make sure that any glue or paper pulp is removed from the bare copper.  Swish it in the water and make sure the copper looks clean.
  • As the toner dries, it's not uncommon for it to turn a bit white.  That's OK.  When it does that, make sure the bare copper spots (to be etched) do NOT have white on them.  If they do, the acid won't etch the copper away.  I usually just keep rubbing with my fingertip until the copper areas remain copper colored when the board is dry.

Etching

  • Dry the board off, and then drop it into a pan of the etchant.
  • It takes about 10 minutes or so for the copper to come off.  Note, the copper turns pink during one stage of the etching.  That's NOT the fiberglass PCB.  The pink copper is still conductive, and needs to be etched away.  Let is soak longer.
  • When it's done, you should see yellow PCB where the etching occurred.
  • Remove from the etchant, and rinse with water.
  • Note, you can save the etchant and reuse it.
  • After etching is done, remove the toner from the board with acetone.  I put some on a few napkins and scrub the board.
  • Rinse the board off again, until it's nice and clean. 
  • Inspect the copper and traces to make sure it all came out right.

Liquid Tin

  • I put liquid tin on the board after etching.  It protects the copper from tarnishing so that soldering will be more effective later.
  • Put the PCB in a dish and pour liquid tin on it.  The copper will turn silver immediately.
  • Rinse with water.  Save the unused liquid tin.

Monday, January 8, 2018

Hojo and the case of the deaf Tentec Omni V

Hojo and the case of the deaf Tentec Omni V



A friend from the local ham club handed me his Tentec Omni V.  He hadn't used it in a while, and was preparing to sell it to another ham when he noticed it appeared to be very weak on receive.  He asked me to take a look.


Power supply


Right off the bat, I checked the power supply.  It was reading low.  The trimmer pot was right next to the activity LED, so I couldn't resist dialing it up to a proper voltage.





Initial problems

I powered up the rig and found the LED display blank.  I opened the covers and wiggled a few things directly behind the LED display.  It flashed back on.  The board was not seated very well, apparently.



At the same time, one of two incandescent bulbs behind the S-meter was intermittent.  I snugged up the fixture with a pair of pliers, and the bulb became more reliable.



Having taken care of those little physical problems, I could finally try injecting a signal into the radio.  Sure enough, it was quite hard of hearing.  I needed a signal of about -20 dbm to be discernible.  I confirmed the problem existed across multiple bands.

Diagnostics

I opened up the radio and started tracing the signal.  I injected a tone on 14 Mhz (1) and followed the path.  It got through the antenna selection circuits and initial attenuator just fine.  It reached the front-end mixer board (2), and exited that board mixed to 9 Mhz (3).  I also verified that the signal disappeared when the injected signal was stopped.  So far so good. The signal passed through the 9 MHZ IF board (3)  -> (4) unmolested and was amplified slightly.  From there, it went into the Pass Band Tuning board (4), and finally into the AF/IF board (5).  There's where the trouble began.



I measured a good signal coming into the board (5 above).  It passes through a few amplifier stages first thing.  I checked at the end of that stage and found no signal.  Tracing back, I found the signal disappeared after passing through a J310 Fet.


I next confirmed that there was power on the "+REG" rail.  It was reading 8.78 volts.  Unfortunately, I had no idea what the voltage was supposed to be.  After about 40 minutes of tracing the +REG rail back to a power board, I found a knockoff sentence in the service manual that +REG should be 8.5 volts.  So, it was a touch high, but shouldn't have been a problem for this situation.

Having narrowed it down to this Fet, I checked my parts bin, and voila, I had a few in stock!

The repair


The board had approximately a jillion connectors on it.  I carefully photographed everything before removing it.


Now that I had a better view of the FET in question, I confirmed the model, and that my parts-bin FET matched.



I pulled the FET and replaced it.







Testing

After putting things back together, the radio was hearing loud and clear.  My Service Monitor can generate signals as low as -130 DBM.  The signal was clearly discernible all the way down.

Talking with the owner, he was satisfied that the repair was sufficient, so I stopped there, rather than attempting to find alignment instructions.

Success!

Monday, January 1, 2018

Zeta FX-79 Buffalo Build - Port planning, Smartport and RSSI

Zeta FX-79 Buffalo Build - Port planning, Smartport and RSSI

Serial Port Planning

The Omnibus F4 Pro V2 offers 3 UARTs.
  • UART1 - Used for SBUS
  • UART6 - Available
  • UART3 / I2C - Available if I2C is not needed
My Ublox Neo-M8N GPS with Compass module will need UART6 for the GPS, and the I2C pins for the Compass functionality.  Unfortunately, I want to do Smartport as well, but don't have a UART.  So, instead, I'll use softserial for the Smartport connection.

My configuration will be as follows:
  • UART1 - Used for SBUS
  • UART6 - GPS
  • UART3 / I2C - Compass
  • Softserial - Smartport

Smartport

The Softserial ports are available on the Omnibus F4 Pro V2 via two pads on the front of the board.  I followed the FrSky SmartPort using SoftwareSerial procedure at the bottom of the Omnibus F4 page.  They call for a 1k ohm resistor between the RX and TX pins.  I had one in 0603 in my parts bin, so I put it between the pads, and then attached a small wire-wrap wire to the RX pin.  It ended up working fine.


I love surface mount soldering!  Here's a 1K 0603 resistor from my parts bin.


There's the resistor, temporarily sitting on top of the processor.  I circled the two pads it's going to be crossing below.


Here's the resistor installed, with a bit of Wire Wrap wire connecting to the RX pin.  

After confirming that all worked well, I went ahead and secured this wire with a dab of glue to the top of the processor chip, for strain relief.

In order to use the Softserial port, it needs to be enabled in the iNav Configurator.

I enabled Softserial, and while I was at it, also enabled telemetry.
After a reboot, I went into the ports page, found the Softserial port, and configured it for Smartport at 57,600 bps, as suggested.

The final step was to go into the CLI and apply two settings:

set smartport_uart_unidir=OFF
set telemetry_inversion=on
Having done that, I was able to go onto my transmitter, do a "discover", and telemetry values populated from the flight controller.

One additional note.  The GPS telemetry does not appear for a "discover" on the radio until you have a GPS lock.  This caused me quite a bit of frustration.  Once the telemetry is discovered, it will be on the list on the radio, thereafter.  You do not have to rediscover it after it is in the list.


RSSI


The Omnibus F4 V2 uses a tiny little pad in the middle of the board to receive RSSI data from the receiver.  Many people are soldering a small wire to the pad, and just hanging it off the Flight Controller, with a bunch of glue and hope holding it in place.  I didn't really like that solution.  Instead, I opted to hijack the RAM pins to make the RSSI externally available.

The RAM pins on the Omnibus F4 pro V2 are wired together, but otherwise isolated, if you do not solder a jumper elsewhere on the board, linking them either to VCC or the 5v rail.  Since I left the jumper undone, the RAM pins are just tied to each other.    Since I'm powering my Camera and VTX via an external wiring harness, the RAM pins are unused.

I soldered a small jumper from the RSSI pad to one of the RAM pins.  I will then solder a heavier gauge wire into the other RAM pin hole, and connect that to the wiring harness going off to the receiver.  This should provide greater strain relieve for the RSSI jumper.  I think this is a much more resilient solution.  Here's the jumper wire going to one of the RAM pins.  This picture does not illustrate the wire harness leading off the board.



One thing to note.  I was concerned that the RSSI pin might have problems.  The MCU on the F4 board is running at 3.3 volts.  The receiver runs at 5 volts.  I was worried that the RSSI pin might be overdriven by the receiver.  I wondered whether a voltage divider would be needed to scale the voltage down.  Fortunately, FrSky had considered this.  The RSSI value is documented to be a PWM value between 0 - 3.3 volts.  So, even though the receiver is running at 5 volts, it is safe to run the wire to the Flight Controller.  So, the result was, I didn't have to do anything special.  I felt better having researched it, however.

Thursday, December 28, 2017

Zeta FX-79 Buffalo Build - Wiring plan

Zeta FX-79 Buffalo Build - Wiring plan


The stock configurations for the FX-79 seem to recommend running with 3s lipos, so that's what I'm planning to do.  I'll be using two 5000 mah batteries in parallel.    My Flight Controller is rated up to 5s, as far as I can tell on the forums, so I have the option of upgrading later if I wish.

The Omnibus F4 Pro V2 comes with an onboard 3 amp BEC.  However, as I read the forums, I understand that it can get rather warm.  As a precaution, I'm planning to run very little through the onboard BEC.

Power Plan

  • Direct from batteries
    • Flight controller - rated to 5s (I think).
    • Runcam and VTX (through a filter).  Both are rated up to 6s.
    • ESC (via the Flight Controller for current measuring)
  • BEC on Flight Controller
    • GPS
    • Uart inverter
  • External BEC
    • Servos
    • Receiver
  • External BEC (auxiliary port)
    • Lights or special effects
I roughed out a wire plan on my office whiteboard.



About Club24A


There are known problems with this board with regards to reporting accurate current if VCC is jumpered into the Camera or VTX pins.  It's known as "Club24A" in the forums.  I'm avoiding that problem entirely by not using the onboard jumper to apply power to the RAM pins.  I'm actually hijacking the RAM pins for another purpose (RSSI).  I'm providing the power to the camera and VTX via the wiring harness, apart from the Omnibus board, so I do not expect any of the Club24A issues.

Riser pins


Since I'm providing power to most of the external devices via the external BEC rather than through the FC, it's led to a rather convoluted wiring harness.  While the FC provides 3 pins in most places, for powered servo cables, I'm generally using just the signal wire, and drawing power from my external BEC.  I decided to solder wires directly onto the Flight Controller and terminate with female servo connectors.  This makes the flight controller a bit of a hassle to wire up, but with care, it should be alright. 

I'm planning to use extension cables everywhere, so that the FC can be removed for calibration or reprogramming if necessary.  Also, being the most convoluted part of the build, it should be fairly easy to transplant, if the plane crashes badly enough that I need to replace the foam.

Here's the flight controller wiring, about 3/4 done.  I'll update this picture when it's complete.  Note the heavy cables which lead to the batteries and ESC.  The servo cables patch into other cables to provide power.



Monday, December 25, 2017

Zeta FX-79 Buffalo Build - Preliminaries

Zeta FX-79 Buffalo Build



My wife bought me a Zeta FX-79 Buffalo flying wing for Christmas.  I'll be chronicling the build in this blog post.  My motivation for this plane was to have an FPV platform to use for balloon "Search and Rescue" operations.  I'd like to have an FPV camera, and a higher resolution camera for post-flight analysis.  I'd also like to be able to fly my Kenwood TH-D72A radio as an airborne APRS digipeater platform.  Since my trackers are very low power (10mw or so), the hope is that an airborne digipeater might be helpful in recovery operations.

I purchased the fittings from a variety of sources.  The Bill of Materials follows. 


I'll be doing the build with the Omnibus F4 Pro V2 flight controller, which includes an OSD and can relay Smartport Telemetry back to my Tanaris Plus transmitter.

In future posts, I'll document the build process and initial flights.