Review: PCBWay circuit board fab and assembly

If you’re in the US, and you don’t have a million bucks to spend, the go-to choice for PCBs is usually OSHPark. OSHPark rocks. There is no minimum spend (you can literally order 3 copies of a $1 board if you want to), no minimum board quantity, and the fab tech is decent for 2/4 layer boards. I myself have ordered more boards from OSHPark than I can count, both for work and for my own projects.

OSHPark is great for standard quick-turn style jobs, and is slowly adding more options, like 2oz copper and their “super swift service”, which is really just an expedited option. But what if you want something special? Aluminum-backed, for example? Or 6+ layers? These options start to fall out of the standard quick-turn price range, which means expensive, nonstandard order from local PCB manufacturers.

PCBWay is a China-based PCB fab and assembly house that I used for the first time around 2 years ago. Similar to OSHPark, they offer affordable pricing on basic boards (2/4 layer, 6/6 trace and spacing, etc). They have a nice online quoting tool where you can set your board specs and add options to get a quick idea of time and cost. They also support a pretty solid set of PCB specs and options, including Flex PCBs, Aluminum-backed PCBs, multilayer (10+), and a multitude of silkscreen colors, dielectrics, and plating options. A big bonus is the availability of fast shipping through DHL. PCBWay has also recently pushed out an EAGLE upload conversion tool, which is a great way to avoid generating gerber files if you use EAGLE. Boards usually show up within 2-3 days of shipping, which is insanely fast coming from literally across the world.

PCB quality is great. I’ve ordered several boards over the span of the last few years, all with different options, and I’ve been impressed. If you stick to the standard options (6 mil min. traces and spacing, 0.3mm via), the boards are affordable and very high quality. The copper features are well-formed, and I haven’t seen any breaks or shorts due to masking errors in the boards I’ve purchased. Silk comes out good, assuming you follow the general rules for silk. Soldermask varies a bit based on color, specifically when it comes to hardness. If you plan to do a lot of manual reworks, or durability is important to you, I’d stick to the standard green. In my experience, white and other lighter colored soldermasks can be a bit soft.

Now let’s talk about board assembly.

Assembly is a notoriously expensive process for prototype boards because of the upfront costs. Board houses don’t want to set up their SMT line for your 10-board run. There are houses which specialize in fast prototype runs, but the caveat is that these are usually extremely expensive or rely on inking a deal for large-scale production after the prototypes have been made. There are plenty of companies that can afford to roll their upfront R&D costs down into the price of the product once it’s mass-produced, but usually SMT assembly is out of reach for small operations or anyone without at least several thousands of dollars in budget.

I’ve been working on the design of a product with the eventual goal of listing it on Massdrop. It’s a small board and is fairly dense, with some hard-to-place parts (USB-C connector, 3x3QFN package USB hub chip). I ordered the first proto PCB from OSHPark and hand-soldered it, and it became pretty obvious I was going to have to find a more sustainable solution for the next run of prototypes. The board itself is just under 100 individual components, with placements on both sides, and no through-holes. I decided to give the PCBWay assembly a try, since their offering seemed like a good fit and is extremely affordable. I decided I’d specify components  and distributor in the BOM and let them purchase components. You can also drop-ship components or mail them directly to them.

As an aside, I’ve got a reasonable amount of experience designing PCBs and in particular, following DFM (design for manufacturability) rules. Following these rules, some of which I’ll detail below, help ensure that the board is easily manufactured and will work when you receive it. The easier it is for an assembler to make your board, it’ll be lower cost with higher yields. Don’t panic if you’re not aware or familiar with these rules, but I would suggest getting familiar before starting a PCB assembly order with any assembler. This project in particular, I benefitted greatly from sticking to DFM rules.

Some PCB design/assembly DFM rules. These are just some of the bigger ones. For a more complete list, check out this document.

  • Place large components, like connectors or ICs, on one side of the board (generally designated the top). Place only small & light components, like passives and SMT LEDs, on the bottom side.
  • Keep all SMT items a minimum distance from the board edge, generally 3mm. Exceptions can be made for items like connectors, but your boards will probably need to be panelized (something PCBWay will happily handle for you).
  • Keep SMT items a minimum distance from each other. This distance varies based on the assembler and their placement process, but at minimum 1mm is a good place to start.
  • Make sure all part footprints contain pin1 callouts in some form, along with reference designators. Polarized 2pin components should have a + marking on their anode. This helps assemblers determine rotation, and also allows them to easily identify parts if they need to ask questions.
  • Ensure (no, really, like your life depends on it) that your BOM is well-detailed and contains specific part numbers along with component descriptions, preferred distributor, distributor PN, and matching reference designators on the PCB. A good BOM will save you hours of headache, and providing a good BOM to PCBWay will actually lower your quoted price, because it reduces work and risk on their end.

The hardest part about assembling a board (or designing one to be assembled, for that matter), is getting the small details right. That’s why putting some thought into how your board is going to go together goes a long way. If there is a chance that a part goes on backward (a diode, LED, or polarized cap), put a marking on the silkscreen to make it clear which way the part should be placed. If you have the room, space your components out more than you need to. It’s also a good idea to make a list of the possible mistakes that would be ‘deal-breakers’ and share these with the assembler before your board is put together. A little bit of thought early on can save you a lot of headache.

To this end, I was very impressed with PCBWay’s ability to ask questions when there were ambiguities. I received several emails with questions around component placements and polarities. Each email was well-worded and contained a marked screenshot of the PCB along with the component in question.

It was easy enough to clear up any uncertainty. In addition, they send ‘approval’ images of the first PCB that comes off their line, so that you can do a rough scan for errors or mistakes. Both of these lead to excellent results; 100% of the assembled PCBs I received were built correctly and were fully functional. Not bad! As mentioned earlier, once the PCBs shipped they arrived stateside quickly with no hassle from customs. DHL is slightly more expensive as a shipping option, but in my experience it’s worth every penny spent.

Here are some assorted images of the panelized/separated PCBs and assemblies I got from PCBWay. The product is called the DB1, and there will be more info in the coming months available here. I might as well mention: If you’re thinking about ordering a PCB or assembly from PCBWay, go ahead and use my referral link for $5 free towards your first purchase. (I receive rewards for use of this link, by the way. I also got a few bucks off of some PCBs for sharing this article with PCBWay, but I wouldn’t have written it if I didn’t love what PCBWay is doing)

Questions? Do you have an experience working with PCBWay? Leave a comment below.

Automatically renewing Let’s Encrypt SSL Certificates on PythonAnywhere

Several months ago, I set up a small webapp using PythonAnywhere. It’s a really easy to use service and for small projects is hard to beat since there is so little setup. They even let you use Let’s Encrypt (albeit in a hackish way) for free SSL certificates!

The problem that I’ve run into is that due to the way Let’s Encrypt is set up on PythonAnywhere, there’s no easy way to configure an auto renewal. Usually, one would just set up a cronjob set to run every 3 months. This doesn’t work for two reasons: the first is that there _is no cron_ on PythonAnywhere, and the other is that you must manually email the PythonAnywhere staff when your certificate is actually updated and they do the “installation”. Multiple times now, I’ve forgotten to renew until someone came to me telling me that the site wouldn’t let them do anything because of an expired certificate.

I got sick of feeling dumb and wrote a simple Python script that automatically renews the certificate and sends an email to the PythonAnywhere staff letting them know that it’s ready to be installed.
It’s scheduled using the PythonAnywhere scheduled tasks, and even though it runs every day, the renewal itself only takes place when the certificate is set to expire within 29 days or less.

If this sounds like something that would be helpful, its on my GitHub page.

Apple’s ‘BatteryGate’: iPhone 6 Performance Before & After a Battery Replacement

I have an iPhone 6 (🦕, I know) and noticed an incredible performance hit when I upgraded to iOS 11. I had never seen such an awful change immediately after installing a software update; when I upgraded the phone to iOS 10, it actually got faster that it was on iOS 9. I knew something had to be wrong but mostly just got used to the degraded performance.

When the Geekbench team discovered that Apple is clocking down older iPhones due to aging batteries, I immediately bought an OEM Apple battery to replace my old one. It was $14 on eBay. Politics aside (I’m not going to rant about whether or not it was right and just of Apple to reduce the performance without telling customers, since there are plenty of other hot takes on the issue itself), I would have happily bought a new battery if I had known that it was going to keep my phone running fast.

I promptly purchased Geekbench 4 and took an initial benchmark to compare with once I replaced the battery. For reference, my battery had just over 600 (!) cycles on it, 100 over Apple’s lifetime recommendation. Geekbench scored the phone at 1059 single-core and 1794 multi-core.

 

After replacing the battery I took another Geekbench benchmark.

In almost perfect agreement with Geekbench’s findings, my iPhone 6 performance increased by nearly 45% single-core, and just over 46% multi-core. Looking at it differently, the phone that I had been using was almost fifty percent less powerful (worse?) than the day it was bought. Unsurprisingly, it feels like a new phone and is very noticeably faster, especially when replying to iMessages or notifications on the lock screen.

Regardless of  your opinion regarding Apple’s throttling of older phones, these results give users a serious reason to spring for a battery replacement.

Update: Apple has responded to this issue and is now providing $29 battery replacements for iPhone 6, 6s, and 7.

Liquid Cooling an ASUS GTX1070 with Using NZXT G12 GPU Cooler & Corsair H75

My buddy recently decided he wanted to watercool his Asus GTX1070. There’s only two real choices: the NZXT G12 bracket (which replace the stock cooler) and the Corsair HG10 N980. The HG10 isn’t actually made for the 10 series, but is apparently easily modified to clear the VRM caps using some power tools. The G12 claims full compatibility with 1070 series cards, so he went with that. He also picked up a Corsair H75 which is fully compatible with the G12 bracket.

After taking the stock cooler off, it was immediately clear that the VRM caps were going to interfere with the curved standoffs the G12 uses to mount to the card.

Nothing a little grinding can’t fix! With a file, some thin washers, and about 15 minutes, it’s easy to modify the G12 to fit this specific card. The main issue here is the flanged lip visible on the bracket below. It hits the top of the VRM caps.

We started by grinding the lip off using a metal file. This only took about 3 minutes; the brackets are relatively soft.

A bit of black sharpie afterward covered it up well enough (these two brackets aren’t visible once the cover is in place).

We also added some very thin washers between the bracket and the topside of the PCB to add the slight extra bit of offset needed to clear the caps. They’re no more than 0.5mm thick.

After making these two changes, everything fit just like it should. We added a thin ring of foam between the white bracket and the H75 to make up for the width the thin washers add, but I think you could get away without doing this.

That’s it! Note that when you’re putting this bracket on, with or without this mod, you should only tighten the thumbscrews until you see the card flex ever so slightly. Once you see this, back each thumbscrew off by about 1 full turn. The cooler does not need to be pressed tightly against the die; over-tightening the cooler will put unnecessary strain on the PCB and components and can lead to premature failure of the card due to thermal cycling. It’s also possible to crack or damage the GPU die.

A few more pictures of the final product:

Downloading & Saving a Nest Cam Live Stream Using a Raspberry Pi + Debian Linux

Tonight, I stumbled on an interesting post on Reddit. It linked to two Nest Cams livestreaming the landfall of hurricane Irma from a Miami condo. I popped the streams open on my phone (the hurricane had not hit yet, and the sky was mostly clear) and thought about the fact that I was probably going to fall asleep before the power went out and killed the stream.

Being an incredibly good-looking and overconfident dweeb, I then thought about the fact that there’s got to be a good way to rip and save a Nest Cam livestream so that I could watch it tomorrow, maybe post a time-lapse, and gain all of the Karma. After all, its already been done with tons of other streams from various sites. It seemed like a decent Friday night hackathon in the making, and would at least solve the problem of me falling asleep lest I fail.

I decided I wanted something that was (1) automatic, and (2) running in the background so that I didn’t need to stay up all night or keep my computer on. I ran through the list of options in my head:

  1. Just stream them in a browser window and use screen capture (lame and fails both #1 and #2),
  2. Use one of the millions of Chrome or Firefox plugins that allows for saving of streams (extra lame and still fails #2),
  3. Use some sort of stream-ripping software built for Linux so I could load it on my always-running Pi (not lame, but impossible for me to find something that worked after looking for an hour or so), or
  4. Hack it and do it myself.

If you haven’t guessed already, I went with #4. I’m going to show you how I figured it out and how to do it yourself. This assumes some basic knowledge of Linux command line and shell scripting.

First thing first, I loaded one of the Nest Cam streams using the links provided on Reddit. The video livestream itself sits inside a Nest-branded HTML page that does this really annoying thing where it auto-pauses and pops over a Nest advertisement every once in a while. If I wasn’t going to rip it out already, I would have been sufficiently annoyed by this to figure out how to get to the base stream.

I poked around inside the page source using the Safari dev tools to see if I could find any obvious stream container or link, but didn’t see anything. I did find a more minimal stream that is formatted for Twitter but it still does the popover thing. Boo. I also poked around in the javascript (warning: there’s a lot) to see if the stream was being lazy-fetched from any obvious source. Again, nothing. Boo.

I decided to use the Timelines tool to see what’s being loaded on the network. I recorded for a few seconds and saw what was clearly a periodic fetch taking place. There’s an XHR request going out approximately every 4 seconds. It’s loading a media_xxxxxxxx_123.ts file and a chunklist_xxxxxxxx.m3u8 file after each request. This is definitely an MPEG-2 stream, with the chunklist serving as a manifest for the media.ts file. Bingo!

.m3u8 files are commonly used to define video streams, and so I knew I was on the right track. Right-clicking on the m3u8 file and choosing “Copy Link Address” and pasting it into the Safari address bar yielded a base-level video stream with no extra junk (*cough*) on top of it. It looks like Nest streams their livestream content from stream-bravo.dropcam.com or from stream-delta.dropcam.com. (Both are currently using Wowza Streaming Engine 4 Subscription Edition 4.7.1 build20635)

The next step was saving the stream using this URL. Time to break out the Pi! I figured I could use ffmpeg to do this, and after a quick google search, my assumptions were confirmed. This StackOverflow question gave me what I needed, except I wanted to ensure that the ffmpeg command was always running (in the event the stream broke up and was restarted, a network issue occurred, etc).

For those of you who just want to save a Nest Cam stream to disk using Raspbian/Raspberry Pi/Debian/Other Linux, this is the command that will do it for you (you need ffmpeg installed in order to use this): ffmpeg -i http://your_stream_chunklist_link.m3u8 -c copy -bsf:a aac_adtstoasc /path/to/output/file.mp4. For example, this is the command I used to save the stream I was watching to my home directory: ffmpeg -i https://stream-delta.dropcam.com/nexus_aac/a8a645a10ef24a50b250c14a08b02ef9/chunklist_w719996219.m3u8 -c copy -bsf:a aac_adtstoasc Stream.mp4

In order to make sure that ffmpeg was always restarted in case of any issues, I whipped up the following shell script (named runStream.sh) to be run as a cronjob:

#!/bin/bash
#make-run.sh
#make sure a process is always running.

process=LivingRoom
now=$(date +%Y%m%d%H%M%S)
makerun="ffmpeg -i https://stream-delta.dropcam.com/nexus_aac/a8a645a10ef24a50b250c14a08b02ef9/chunklist_w719996219.m3u8 -c copy -bsf:a aac_adtstoasc /media/HDD/Stream_$now.mp4"

if ps ax | grep -v grep | grep $process > /dev/null
then
 exit
else
 $makerun &
fi

exit

The script checks to see if the ffmpeg command is running using ps ax and grep. If it is, there is no need to start it, so it exits. If it isn’t, the script is started using the makerun shell command. Note the $now variable at the end of the filename: it automatically appends a puncuation-less timestamp to each video file, so that the previous file is not lost when ffmpeg is automatically restarted.

The last thing to do was to make the script executable using chmod +x runStream.sh and add it to the crontab using crontab -e. I set it to run every minute (can’t miss any of the action!) using the following crontab:

# m h  dom mon dow   command
* * * * * /home/pi/runStream.sh

After saving the changes and waiting a minute, I saw the first video file pop up. After running for a few hours, the auto-restart was a great idea, because it’s kicked in several times (likely due to haphazard internet because there a HURRICANE).

Stay safe out there, Florida. It’s going to get crazy.

DIY LED Lighting for Fishtank/Aquarium Setups

I’ve got an older Marineland Eclipse 3 tank that needed a new CFL bulb (the old one was barely igniting, and the spectrum was all sorts of off). After looking into the cost for a new bulb from Mainland, I decided I would rather do a simple DIY LED upgrade than pay for the same underwhelming fluorescent bulb.

I purchased a 5M string of cool white LEDs from Amazon. They’re the 3528 size, 60 LED/meter. The important thing is that they’re the resin-coated waterproof variety. I went with the cool white for two reasons: the spectrum is better for underwater plants*, and as the resin heats and ages, it yellows slightly, making the light a bit more yellow, or warm.

I also had some of the double-sided reflective foam lying around from a previous project. Although you could do this upgrade without it, it makes for an even more light-efficient setup, as light reflected off of the water or bottom of the tank is reflected back into the tank.

The first step is to remove the previous lighting setup. The Marineland CFL bulb is held inside the hood with a few screws, so it’s easy to remove. Next, I sized out the reflective foam and glued it inside the hood using hot glue.  It’s important to make cutouts for any hinged openings!

Next, I glued the LED strip in a “folded” pattern. This isn’t as clean as actually cutting the strip and taping them truly parallel, but it keeps the waterproofing intact and really reduced the amount of work needed. Soldering this waterproof strip takes a lot more work than you would think, so don’t do it unless you really need to. I used hot glue in addition to the adhesive backing on the LEDs since the adhesive isn’t super strong.

Finally (not pictured), I covered the LEDs with a few passes of packing tape and made sure everything was stuck down nicely. This isn’t required, but is a nice bit of peace of mind for when it’s all powered on. The LED strips are supposed to be waterproof, but an extra layer of protection keeping it away from the water in case the glue fails can’t hurt.

And that’s it! It looks great when it’s powered on, emits way less heat, and uses less energy than my expensive Marineland bulbs did. Not to mention that I can reduce the hours it’s on for, since the lighting is more efficient for plant growth.

*Tech-overload sidenote: Cool white LEDs are actually not  emitting white light. They work by using a blue-emitting diode coated with a yellow/red coating (usually phosphorous-based) which, combined with the blue, looks white. Because of this, the spectrum of a “white” LED actually peaks highly in the blue (~450nm) with a broader peak in the red (~550-700 nm). It just so happens that the most important frequency for Chlorophyl A is right around 450nm, with a secondary peak around 700nm.

Analyzing the Performance of Acorns Investment Portfolios using Quantopian

I’ve been using Acorns, the app to “help anyone invest” for a little over a year, mainly as a curiosity. One common theme I’ve seen amongst Acorns users is the disappointment with the lack of returns throughout their usage. Simply searching “Acorns Returns” online brings up hundreds of posts (many of them, uncoincidentally, occurring on r/investing*). While many chalk down to unrealistic users irritated they haven’t seen their investment quadrupling in value overnight, reading these posts did make me question: what historical data in regards to returns is available for the different Acorns portfolios?

Digging around, I quickly realized the answer was: not much. Acorns itself publishes no info on the rate of returns of its various portfolios, which isn’t unexpected or surprising. Reading the accounts of other users doesn’t cut it for me, since many of them bail after getting antsy or taking their first 2% loss. There’s another camp of users who claim they’ve seen some absurd rate of return (Bro, I’ve seen 65% since May!). What about portfolio statistics? Dreaming of reading about portfolio volatility? Keep dreaming. Point being, tangible data is hard to come by.

Coincidentally, I’ve been playing around with Quantopian, an absolutely awesome python-based algorithmic investing framework and simulation/backtesting platform. Quantopian has access to stock metrics, such as price, volume, dividend/split info, etc, since January 3rd, 2002; Quantopian allows users to backtest stocks and trading algorithms using this data, in order to examine performance of various trading strategies. Somewhere along the line, I had the thought: “Why not just create each of the Acorns portfolios and backtest them to analyze their performance?”. The data provided by these backtests is generally so close to true performance for simple cases that any error is excusable.

Acorns allows users to choose a portfolio based on their risk tolerance and investing goals, all ranging from “Conservative” to “Aggressive”. The five portfolios are really just different target allocations in six ETFs: VOO, VB, VWO, VNQ, LQD, and SHY. The first four funds are Vanguard ETFs and the last two are iShares bond ETFs. The portfolio distributions are shown below, with the Aggressive portfolio being shown on top and the Conservative portfolio on the bottom.

acorns

This is basically all the info required to build these portfolios within Quantopian. Each security in the portfolios is added to the trading list, and  a rebalance function is scheduled to run every day 1 hour after the market opens. In reality, the Acorns portfolio rely on new round-ups and contributions to keep allocations at the right percentages, but this method of simulation seems close enough. I also set the backtest to only hold long positions, and made sure the trading commissions were zero (so as to only focus on the portfolio performance, and not the Acorns fee breakdown). The entire algorithm is shown below.

"""
Attempts to model the Acorns Agressive Portfolio for performance modeling.
Backtesting is limited to September 10, 2010 (first trade day of $VOO).
Acorns keeps allocations exact by purchasing fractional shares. Since this isn't generally possible, a larger initial capital must be used (>$10K).
"""

def initialize(context):
 """
 Called once at the start of the algorithm.
 """ 
 set_long_only()
 
 # Each security in the ETF along with the target percentage
 context.voo = (sid(40107), 0.14) 
 context.vb  = (sid(25899), 0.25) 
 context.vwo = (sid(27102), 0.20) 
 context.vnq = (sid(26669), 0.30) 
 context.shy = (sid(23911), 0.05) 
 context.lqd = (sid(23881), 0.06) 
 
 context.security_list = [context.voo, context.vb, context.vwo, context.vnq, context.shy, context.lqd]
 
 # Rebalance every day, 1 hour after market open.
 # In reality, the Acorns app banks on additional buys 
 # To keep the allocations correct.
 schedule_function(rebalance, date_rules.every_day(), time_rules.market_open(hours=1))
 
 # Fees are zero, if you're on the "student plan". 
 set_commission(commission.PerShare(cost=0, min_trade_cost=0))
 set_commission(commission.PerTrade(cost=0))
 
 
def rebalance(context,data):
 """
 Execute orders according to our schedule_function() timing. 
 """
 for security in context.security_list:
   #rebalance to target percentage
   if data.can_trade(security[0]):
     log.info("Rebalancing %s to %s percent" % (str(security[0]), str(security[1])))
     order_target_percent(security[0], security[1])

The only thing that changes between each of the Acorns portfolios is the target percentages. Everything else remains constant, which makes backtesting and examining the different portfolios incredibly easy. Three backtest start dates were chosen: September 10th, 2010, September 10th 2014, and October 8th 2015. Each backtest ended on the same date: October 7th 2016. The returns (%) for each backtesting window are summarized below.

screen-shot-2016-10-09-at-1-04-39-am

Upon first look, it appears that the returns are reasonable and on par for each portfolio description, and I can confirm that the returns are very close to the actual returns I’ve experienced during my usage of the app over the last year. Returns diminish fairly monotonically from aggressive to moderate as one would expect.

There’s a catch though: I didn’t include the S&P500 benchmark performance in the results.

screen-shot-2016-10-09-at-1-12-23-am

In all the backtest windows, the S&P500 destroys the Acorns portfolios. Suddenly, the 10.57% short term return doesn’t seem so great after realizing the S&P500 did almost 13 percent in the same year. Take a look at the Quantopian backtest results for the aggressive portfolio, the best-performing one of the bunch, over the last year.

screen-shot-2016-10-09-at-1-16-59-am

The portfolio almost exclusively underperforms the S&P500 in terms of returns. It’s also worth nothing that the drawdown during the January-Feburary time period of 2016 is worse than that of the S&P500 as well.

At this point, the logical thought is “Of course the drawdown sucks, because this is the Aggressive portfolio.” And you’d be partially right. Take a look at the results for the conservative portfolio during the same time period:

screen-shot-2016-10-09-at-1-21-52-am

The drawdown during the same period is hardly better than the S&P500, which fell over 3% from the starting point in the beginning of October 2015. Rather disappointing, given that the portfolio is billed as a “safer bet” option for investors. The backtest results over the last year of all five portfolios are given below, for anyone interested.

screen-shot-2016-10-09-at-1-16-59-am screen-shot-2016-10-09-at-1-40-41-am

screen-shot-2016-10-09-at-1-41-00-am

screen-shot-2016-10-09-at-1-41-20-am

screen-shot-2016-10-09-at-1-40-16-am

My final thoughts? It’s not that these portfolios are bad. They’re decent portfolios, especially for beginning investors who are looking to stash away a few pennies here and there with minimal effort and a simple fee structure. Is a 6-10% return better than holding onto a pile of cash and losing out to inflation? Absolutely! An Acorns portfolio serves as an excellent “baby’s first investment”. I just can’t help but shake the disappointment that all five portfolios have underperformed the market in most, if not all, previous financial circumstances. For anyone beyond the novice saver/pocket-change investor, it makes more sense to invest wisely in a few basic ETFs (i.e. NOBL/UPRO) and call it a day. A true zero-fee investment experience can be had by using Robinhood, potentially saving the $1 per month fee. Smart savers who appreciate the auto round-up feature of Acorns can enjoy Wealthfront‘s auto-deposit feature with the added benefit of Tax-Loss harvesting.

Do you have thoughts on my analysis of these portfolios or my backtesting strategy? Let me know in the comments. I appreciate reader input.

*r/investing deserves a post of its own. Similar to r/fitness, the “beginners pretending to be experts” culture leads to an unsurprising amount of awful advice. Trust the internet at your own risk. Trust the internet with investment guidance at your bank account’s risk.

Adding a buzzer beeper to the Illuminati32/Tarot Naze 32 Flight Controller

My buddy just bought an Illuminati32 FC board from Hobbyking for his new ZMR180 miniquad. The board is pretty sweet: Naze32, MWOSD, 35x35mm form factor, and only 20 bucks (on sale). It was really easy to set up, especially with the ZMR180 PDB that Diatone is shipping. The biggest problem is that there’s no buzzer output! A buzzer driver is one basic necessity every flight controller should come with. Low battery, lost models, and mode change beeps are pretty crucial to the operation of these miniquads. Luckily, it’s relatively simple and very cheap to add one onto this board. You’ll need:

  • High gauge (>30) wire
  • Fine solder and a fine-tipped soldering iron
  • An NPN BJT (2N3904, BC557, etc)
  • A 100Ω resistor
  • Heatshrink tubing or tape

The built-in Naze32 (rev5 or greater) buzzer driver is a NPN transistor used in an open-collector configuration. There is a base resistor on the order of 100Ω used to set the drive current. PA12, or Pin 33 of the STM32 is used to drive the BJT.

buzz

It’s possible to do this BEAM style, with SMT components and a small piece of protoboard, or even a with small PCB (perhaps from OSHPark). We chose to do it with a little PCB that’s also got an ATTiny84 on it which controls some RGB led strip.

buzz2

The hardest part of this is soldering onto the STM32, since PA12 isn’t broken out or used for anything else. See the below image to find the pin you need to solder to. Note that the text on the STM32 isn’t guaranteed to be upright, so look for the pin 1 marker!!!

stm

You’ll need some fine gauge wire (30 gauge wire wrap wire worked well), a fine-tipped soldering iron, and a steady hand.

IMG_0593

Once the solder connection is made, don’t hesitate to dump some hot glue onto the connection to keep it from being broken loose. Once you’ve got the connection to PA12, solder one end of the 100Ω resistor to the PA12 wire, and the other to the base of the BJT. Solder the positive lead of your buzzer to the flight controller’s 5V input, and the negative lead of the buzzer to the collector of the BJT. Finally, solder the emitter of the BJT to ground. Be sure to wrap everything in heatshrink or tape so that you don’t accidentally short anything out.

That’s all there is to it! Just plug in a battery without turning on the transmitter and the FC should issue the “no connection” beep if everything worked.

Eachine H8 Quadcopter Custom Firmware Rates/Settings

I recently flashed my new Eachine H8 with some custom firmware (Silver13’s CFW, to be particular) and I spent some time tuning it to be flyable in acro mode with the stock remote. Overall, I think this firmware is super cool and really strokes my roots as a hardware engineer and reverse engineer. Not to mention, it flies as good or better than the stock firmware and is tons of fun to tinker with! Here’s my config.h file, for those who might be interested in a good place to start:

//config.h, edited by jaygreco

#include "defines.h"

// rate pids in pid.c
// angle pids in apid.h ( they control the rate pids)
// yaw is the same for both modes

// not including the "f" after float numbers will give a warning
// it will still work

// rate in deg/sec
// for low rates ( acro mode)
#define MAX_RATE 180.0f
#define MAX_RATEYAW 200.0f

// multiplier for high rates
// devo/module uses high rates only
#define HIRATEMULTI 3.0f
#define HIRATEMULTIYAW 4.0f

// max angle for level mode (in degrees)
// low and high rates(angle?)
#define MAX_ANGLE_LO 35.0f
#define MAX_ANGLE_HI 55.0f

// max rate for rate pid in level mode
// this should usually not change unless faster / slower response is desired.
#define LEVEL_MAX_RATE_LO 360.0f
#define LEVEL_MAX_RATE_HI 360.0f

// disable inbuilt expo functions
//#define DISABLE_EXPO

// use if your tx has no expo function
// also comment out DISABLE_EXPO to use
// -1 to 1 , 0 = no exp
// positive = less sensitive near center 
#define EXPO_XY 0.6f
#define EXPO_YAW 0.25f


// Hardware gyro LPF filter frequency
// gyro filter 0 = 260hz
// gyro filter 1 = 184hz
// gyro filter 2 = 94hz
// gyro filter 3 = 42hz
// 4 , 5, 6
#define GYRO_LOW_PASS_FILTER 3

// software gyro lpf ( iir )
// set only one below
//#define SOFT_LPF_1ST_023HZ
//#define SOFT_LPF_1ST_043HZ
//#define SOFT_LPF_1ST_100HZ
//#define SOFT_LPF_2ND_043HZ
//#define SOFT_LPF_2ND_088HZ
//#define SOFT_LPF_4TH_088HZ
//#define SOFT_LPF_4TH_160HZ
//#define SOFT_LPF_4TH_250HZ
#define SOFT_LPF_NONE

// this works only on newer boards (non mpu-6050)
// on older boards the hw gyro setting controls the acc as well
#define ACC_LOW_PASS_FILTER 5


// Headless mode
// Only in acro mode
// 0 - flip 
// 1 - expert
// 2 - headfree
// 3 - headingreturn
// 4 - AUX1 ( gestures <<v and >>v)
// 5 - AUX2+ ( none )
// 6 - Pitch trims
// 7 - Roll trims
// 8 - Throttle trims
// 9 - Yaw trims
// 10 - on always
// 11 - off always
// CH_ON , CH_OFF , CH_FLIP , CH_EXPERT
// CH_HEADFREE , CH_RTH , CH_AUX1 , CH_AUX2 , CH_AUX3 , CH_AUX4
// CH_PIT_TRIM, CH_RLL_TRIM, CH_THR_TRIM, CH_YAW_TRIM
#define HEADLESSMODE CH_OFF


// rates / expert mode
// 0 - flip 
// 1 - expert
// 2 - headfree
// 3 - headingreturn
// 4 - AUX1 ( gestures <<v and >>v)
// 5 - AUX2+ ( none )
// 6 - Pitch trims
// 7 - Roll trims
// 8 - Throttle trims
// 9 - Yaw trims
// 10 - on always
// 11 - off always
// CH_ON , CH_OFF , CH_FLIP , CH_EXPERT
// CH_HEADFREE , CH_RTH , CH_AUX1 , CH_AUX2 , CH_AUX3 , CH_AUX4
// CH_PIT_TRIM, CH_RLL_TRIM
#define RATES 1


// level / acro mode switch
// CH_AUX1 = gestures
// 0 - flip 
// 1 - expert
// 2 - headfree
// 3 - headingreturn
// 4 - AUX1 ( gestures <<v and >>v)
// 5 - AUX2+ ( none )
// 6 - Pitch trims
// 7 - Roll trims
// 8 - Throttle trims
// 9 - Yaw trims
// 10 - on always
// 11 - off always
// CH_ON , CH_OFF , CH_FLIP , CH_EXPERT
// CH_HEADFREE , CH_RTH , CH_AUX1 , CH_AUX2 , CH_AUX3 , CH_AUX4
// CH_PIT_TRIM, CH_RLL_TRIM
#define LEVELMODE CH_AUX1

// channel to initiate automatic flip
#define STARTFLIP CH_FLIP

// aux1 channel starts on if this is defined, otherwise off.
#define AUX1_START_ON

// use yaw/pitch instead of roll/pitch for gestures
//#define GESTURES_USE_YAW

// comment out if not using ( disables trim as channels, will still work with stock tx except that feature )
#define USE_STOCK_TX

// automatically remove center bias ( needs throttle off for 1 second )
#define STOCK_TX_AUTOCENTER

// throttle angle compensation in level mode
// comment out to disable
#define AUTO_THROTTLE

// enable auto throttle in acro mode if enabled above
// should be used if no flipping is performed
// 0 / 1 ( off / on )
#define AUTO_THROTTLE_ACRO_MODE 0


// enable auto lower throttle near max throttle to keep control
// comment out to disable
//#define MIX_LOWER_THROTTLE

// options for mix throttle lowering if enabled
// 0 - 100 range ( 100 = full reduction / 0 = no reduction )
#define MIX_THROTTLE_REDUCTION_PERCENT 100
// lpf (exponential) shape if on, othewise linear
//#define MIX_THROTTLE_FILTER_LPF

// battery saver ( only at powerup )
// does not start software if battery is too low
// flashes 2 times repeatedly at startup
#define STOP_LOWBATTERY

// under this voltage the software will not start 
// if STOP_LOWBATTERY is defined above
#define STOP_LOWBATTERY_TRESH 3.3f

// voltage too start warning
// volts
#define VBATTLOW 3.5f

// compensation for battery voltage vs throttle drop
// increase if battery low comes on at max throttle
// decrease if battery low warning goes away at high throttle
// in volts
#define VDROP_FACTOR 0.60f

// voltage hysteresys
// in volts
#define HYST 0.10f


// enable motor filter
// hanning 3 sample fir filter
#define MOTOR_FILTER


// clip feedforward attempts to resolve issues that occur near full throttle
//#define CLIP_FF

// motor transient correction applied to throttle stick
//#define THROTTLE_TRANSIENT_COMPENSATION

// motor curve to use
// the pwm frequency has to be set independently
#define MOTOR_CURVE_NONE
//#define MOTOR_CURVE_6MM_490HZ
//#define MOTOR_CURVE_85MM_8KHZ
//#define MOTOR_CURVE_85MM_32KHZ

// pwm frequency for motor control
// a higher frequency makes the motors more linear
//#define PWM_490HZ
//#define PWM_8KHZ
#define PWM_16KHZ
//#define PWM_24KHZ
//#define PWM_32KHZ

// failsafe time in uS
#define FAILSAFETIME 1000000 // one second


// level mode "manual" trims ( in degrees)
// pitch positive forward
// roll positive right
#define TRIM_PITCH 0.0f
#define TRIM_ROLL 1.0f


// ########################################
// things that are experimental / old / etc
// do not change things below

// invert yaw pid for hubsan motors
//#define INVERT_YAW_PID

//some debug stuff
//#define DEBUG

// disable motors for testing
//#define NOMOTORS

// enable serial out on back-left LED
//#define SERIAL


// enable motors if pitch / roll controls off center (at zero throttle)
// possible values: 0 / 1
#define ENABLESTIX 0

// only for compilers other than gcc
#ifndef __GNUC__

#pragma diag_warning 1035 , 177 , 4017

#pragma diag_error 260

#endif
// --fpmode=fast ON

Eagle Keyboard Commands for Showing and Hiding Layers

Here’s a short post but one that I think a few people might find useful: CadSoft EAGLE keyboard commands/shortcuts (strangely found under the “Assign” menu in Eagle) to show and hide the Top and Bottom layers independently. I’ve also got one to show all layers, top and bottom. Note that you can change any key a command is bound to when you set up the “assignment” in Eagle.

To set a shortcut in Eagle, choose Options → Assign…. and press the New button.

Screen Shot 2016-04-05 at 8.54.04 PM +  Screen Shot 2016-04-05 at 8.56.32 PM

Once in the menu, choose the key you want the assignment to bind to, along with any modifier keys (Alt, Shift, etc). I chose Alt+0 for showing the bottom layer only, Alt+1 for the top layer only, and F12 for all top and bottom layers.

Screen Shot 2016-04-05 at 8.55.16 PM

Here’s the three Eagle commands for Top, Bottom, and All layers:

  • Top only: display none; display 1 17 18 19 20 21 23 25 27 29 39 41 45 51;
  • Bottom only: display none; display 16 17 18 19 20 22 24 26 28 30 40 42 45 52;
  • All (Top and Bottom): display none; display 1 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 39 40 41 42 45 51 52;

A bit time consuming to implement, but they’ll save you tons of time once you’re actually doing a layout.