Ultra-light bird tracking tag by TvE build log

A bit over a month ago I restarted my tiny-hardware hacking with the goal to build an open source RF tag to track birds. The tag should be able to collect some sensor data and transmit the data over RF. The whole thing is supposed to weigh as little as possible, the goal being sub 1 gram including battery, waterproofing, and harness. The use of the tag would be in the context of the Motus int’l bird tracking network and it would be used by ornithologists (not me torturing backyard birds).

I’ll follow up with a background post (that’s going to be long) but the target feature set for the first tag is:

  • stm32wle5 microcontroller
  • 433Mhz FSK communication (Motus compatible)
  • accelerometer
  • barometer/altimeter
  • rechargeable battery
  • solar cell

I threw everything into Kicad and out came a prototype from Osh Park:

The idea of the prototype is that the right half with the solar cell and battery would be placed on the back of the left half on the real tag. Having everything on one side for a first prototype makes it easier to test and hack.

The stm32wle5 is pretty obvious in this photo and the RF section is below it, plus a footprint for an accelerometer, which is not populated on this board. The upper part behind the red/black wires has the bq25504 energy harvesting chip, which uses the solar cell power to charge the Lithium Manganese MS621FE battery.

The main part I wanted to test was the power section. How the charging from solar works and how the battery behaves when transmitting. These tiny batteries are really made to power RTC clocks or other stuff that sips uA of power, not mA of TX power. First I looked at the power required by a transmission. The following scope capture shows the current (blue trace) for transmissions at 14dBm, 10dBm, 5dBm, 0dBm, -5dBm, and -9dBm. That’s 20mA for 14dBm down to 7mA at -9dBm. My conclusion was that 0dBm is the lowest worth going and 5dBm looks like a good spot to aim for.

Next with battery. Well, actually, the battery can’t power that. It’s ESR is>80Ohm. So the board has about 30uF of capacitance and it’s really the capacitor that powers the transmission. The following scope shot shows a wakeup cycle with one transmission (that’s where the blue current trace maxes out). The cyan trace shows the voltage, which drops dramatically during transmission as the capacitor discharges. Afterwards the voltage climbs up slowly again as the battery recharges the cap.

The packet sent in these test cases has just 5 bytes of payload, longer packets cause more voltage drop or need more capacitance. There’s a tradeoff with having more capacitance, which is leakage. The max kind’a is a 300uF tantalum capacitor, which is available in a 1210 size, so doesn’t yet weigh too much, and leaks ~3uA(!). Going bigger means more leakage and pretty quickly that drains the battery overnight!

I had a lot of ‘fun’ with the bq25504’s undervoltage lockout. As you can guess, I had it too high and the voltage sag during TX triggered it. And then getting the chip to turn on again is rather tricky. I’m still not sure I understand all of it…

The solar cell is pretty amazing. It weighs 0.09g and seems to charge the battery pretty rapidly even in overcast conditions. I didn’t do detailed tests 'cause I realized I didn’t like the LiMn batteries at all.

The TX RF section worked out and I could receive packets using the std Motus receivers. That’s all I really wanted to know as I hadn’t spent much time optimizing that aspect.

The weight of all the parts except for the PCB were in-line with what I expected. The PCB, being 1.6mm thick is way too heavy and of course way too large in this configuration.

So overall some good lessons learned and ready for a prototype “real tag” that actually weighs ~1g.

6 Likes

Very cool project, how to you plan to reduce the weight to 1g? I imagine size must be a difficult thing as well!

1 Like

This is what V2 looks like:

The weight comes in at 0.8g without antenna, epoxy coating, or harness. So probably in the 1.2g range once these are added. The PCB still has some space that could be optimized away, I didn’t want to push too hard, it’s already way too dense for a prototype!

The design is almost identical to V1 except that it uses a FlexPCB for weight reduction and an LTO battery. JLCPCB has a relatively new FlexPCB offering and some promotions, which made this a worthwhile experiment. I spec’ed a full-size stiffener so it’s not quite as floppy as it would be otherwise. This is what he back looks like with solar cell and battery:

The main question I wanted to answer is whether I could actually make something that has all the functionality and weighs less than a gram. Check.

Second I wanted to find out how flexible the FlexPCB ends up being with full-size stiffener (it’s the dark brown portion on the back side). The answer is “meh”. I’ve been messing with one of the tags for over a week, soldering/desoldering components, attaching VNA leads, attaching wires to be able to probe, etc and it’s still working. But it does flex more than I’d like. I have not tried to put a drop of epoxy onto it (and possibly some fiberglass veil) to make it stiffer.

Third I wanted to see how designing a FlexPCB differs from regular PCB. The JLCPCB version has an ultra-slim “core”, which places the top&bottom copper very close together with the result that it’s impossible to have a 50-ohm stripline. Dunno how much it matters in this type of all-jammed-up design at 433Mhz (possibly 915Mhz). The coverlay also has coarser design rules resulting in big openings for all the ICs, that has worked fine for my hand soldering. Dunno about automated assembly.

Fourth, I wanted to test the idea of using an FPC connector to insert the tag into a programmer. I got an FPC connector that doesn’t have a fiddly latch and relies on pressure instead. The result is a no-go. The force required is too big and I have to be really careful not to fold the tag in two when inserting. Next design will use some tiny mezzanine connectors…

Finally I wanted to see how hand soldering 0402 components turns out, I had only dealt with 0603 before. This ended up being quite OK. Patience and magnification are key. The worst part is ensuring that the components don’t get lost between the tape and the PCB!

I also had OshPark make a couple using their FlexPCB, which uses soldermask instead of coverlay. 50 ohm traces are much more reasonable with their stackup which has a much thicker core, but they don’t offer stiffeners. The result is a slightly more flexible tag. Hmmm.

In the end I don’t think that FlexPCB will be the way to go, unless a drop or two of epoxy make a miracle. The ideal is probably a 0.4mm PCB, maybe even a tad thinner. Unfortunately getting anything made is 5x the price of the FlexPCBs and since this is coming straight out of my pocket I’ll hold off 'til I have everything else sorted out. JLCPCB has a 0.6mm offering that is not quite as pricey, so that may be what I use for V3. At that point it becomes pretty easy to calculate the weight savings that a 0.4mm would bring and the cost is really a matter of qty.

Then there’s making this thing work…

I’ve had almost a week of “fun” with the solar charging of the battery. It works great except if the battery gets discharged below 1.7V. Then it wouldn’t charge back up above about 1.75V. Turns out the 1.5V switch mode power supply (SMPS) of the stm32wle5 starts oscillating when the voltage reaches about 1.74V and then stabilizes (stops oscillating 'cause it’s disabled) around 1.78V. Oscillating means that it dumps charge at 4kHz into its inductor-capacitor circuit. The wimpy 4mW solar cell can’t overcome this and the whole thing treads water, never passing above 1.78V.

The proper fix is to add 3 resistors to get a power-good signal out of the bq25504 energy harvesting chip and then drive a high-side power switch to keep the stm32 unpowered 'til the voltage goes above at least 1.8V. But I’m reluctant to add all that size + weight to handle a condition that “should never happen”. After a lot of messing with the tag I figured out that if I change the capacitance that is in parallel with the solar cell from 4.7uF to ~15uF the solar can overcome the oscillations when in full sun. There’s a slight downside in that it lowers the MPPT tracking point at very low light levels but right now I’m gonna go with this solution and see what I see as I continue to test…

I’m now trying to learn how to measure and optimize the whole RF section using a VNA. There are lots of antenna tuning tutorials out there, but nothing about how to handle the RF section of one of these transceiver ICs. If anyone has a pointer to a relevant doc or video, please LMK!

Basically the IC outputs a more or less square wave at the intended TX frequency with some oddball impedance and the external network has to impedance match and band filter. First thing I realized is that getting the right coax to attach to 0402 components is critical. Then there’s the whole workflow of how to calibrate those bare coax “test leads”, attach then to the PCB, etc. Every step requires some soldering, so there’s no quick popping off to recalibrate for a different frequency span and popping them back on. Lots of work-in-progress!

3 Likes

Sexy :smiley: I really like the design. Especially the helper board :slight_smile: You are using black magic probe fw on the helper board or what does BLKMGC stands for ?

When will you opensource the design and fw?

1 Like

Thanks for the kind comment!

BMP has been a roller coaster for me (over the years). In principle I love it but I often have problems with it. The SWD timing differs from the ST-Link one and I just run into trouble. I often use an isolation board with ADUM5401 so I can measure uA and it’s hit or miss in getting it to work, ST-LInk is often better. Right now I use the serial flashing option 'cause this way I only have two unidirectional wires to isolate.

The design is intended to be open source but I have to resolve some political issues having to do with existing vendors that I don’t want to give a heart attack to… I certainly have no commercial goals, I’m retired for a reason.

The HW design is actually quite simple (which is the difficult part…). Basically bq25504 energy harvesting stuff, then stm32wle5 support stuff and its RF section, then I2C accelerometer and barometer (LIS2DH12TR and LPS22HB). All pretty much “typical application circuit” stuff). The SW is currently a mess as I’m using arduino (stm32duino specifically) and it’s so bloated my mind blows every time I dig into details, which I have to 'cause sh*t don’t work. But it’s convenient for quickly testing something… I don’t know what the final SW will look like…

Schematic of V2:

This a quick shot of hooking up the VNA to the input of the filter network:

In the bottom right is an improvised 50 ohm load calibration thingie (47+3.3ohm resistors :crazy_face:). Calibration entails unsoldering the pigtail from the PCB, calibration open, then soldering the two ends together to calibrate short, then soldering to the 50 ohm load to calibrate against that, then soldering back onto the tag PCB :face_with_diagonal_mouth:. Dunno whether there’s a simpler way…

I’ve also ordered some cables with the thinner 1.1mm coax to see whether that’s easier to use in this context. This after many years of always seeking out pigtails with rg178 to reduce losses…

Note that I’m likely doing it all wrong… gotta learn somehow…

1 Like

Very cool project! What I’ve done in the past for VNA work is to calibrate at the last SMA connection and then connect the pigtail and use the VNA port extension option which compensates for the extra cable length and line loss. It’s not perfect but it’s pretty good if you use a short pigtail.

1 Like

Great impressive work!

If your programming FPC connection is for development and final firmware loading, you might consider making a much bigger and more robust flap that you cut off before final deployment? You could keep the existing tail and extend past where the shape currently ends before flaring open to, say, a 2x6 0.1 header pattern. As long as there’s copper only on one layer, cutting the traces through the traces will be fine.
Just be careful if there’s copper on both sides: I once had a prototype design with an optional docking connector tail – the units I built after cutting the tail were fine, but my client used cheap and rough scissors to build out a few more units and were shorting layers together.

You might need to go to a cross-hatched plane to hit your target 50 ohms. I just recently was offered a demo/eval license of Polar and they specifically added the hatch calculation option to the eval license, and I intend to get some stackups calculated while I can!

1 Like

Thanks for the suggestions! Daniel, how short is a short pigtail at 900Mhz? Maybe you have a snapshot of what your pigtails look like you can post? I continue to fiddle with my nanovna (v2.2) and we’ve become acquainted but not yet friends…

I made too many rookie mistakes in the RF section layout, so I think I need to have some std PCBs made with better trace sizes and component spacing so I can play around more easily and get the hang of measuring all the parts.

I’m also wondering about using a Johanson 0400LP15A0122001E low pass filter (https://www.johansontechnology.com/datasheets/0400LP15A0122/0400LP15A0122.pdf). It doesn’t save a ton of components and I still have to impedance match from the uC TX pin to the filter but maybe it does save some headaches… I wish there was an IPD for 433Mhz like there is for 915…

Cutting off the programming flap is a good idea and thanks for the tip about removing the ground plane! I’d like to be able to use the connector to get data off a recovered tag and to do last minute configuration/recharging before deployment, but it’s definitely an option to keep in mind.

I read about the cross-hatching, sounds like it’s a mixed bag due to the effects on the return path. But probably still the lesser evil overall.

Well, you might want to leave the small flap that you currently have so that you can still easily use it with a FPC connector. What I had in mind was to extend beyond that point with the addition to go to the larger connector, at least while you’re making your early units.

1 Like

Got it, excellent idea!

Unfortunately I don’t have any pigtail pictures that I would be allowed to show. 6-8cm should be fine for 900MHz.

1 Like

Time for me to stop fiddling and collect lessons learned + next steps.

  • what I have, i.e., mostly copying the ref design, seems plausible, but the notch filter for the 2nd harmonic is a bit too high
  • changing values and having test leads attached on 0402 components on a poorly laid out FlexPCB is beyond my abilities: I spend most of my time chasing false problems caused by components lifting off their pads, traces lifting, and other snafus
  • I need to use a design that has an RF switch instead of trying to use the switch-less ref design, once I’m comfortable with optimizing that I can decide whether to try switch-less (I don’t really understand the RF tradeoffs)
  • I need to gear up a bit: making more flexible test leads, pigtails, and having a few more intermediate component values at hand to swap in

One thing I don’t understand:

  • the uC TX has some complex impedance, it’s around 10+j2 at 915Mhz and ST doesn’t provide any info about 433Mhz
  • if I try to back out their 433Mhz ref design components I get 48+j25, mhhh
  • in any case, how do I interpret VNA SWR if I’m attaching to the uC RF TX trace, do I calculate expected best-possible SWR based on the mismatch between 50 Ohm VNA and uC and discount that from measurements?

Next steps:

  • design a board I can fiddle with more easily so I can fool around and make friends with all this stuff…
  • try measure actual delivered power and harmonics using SA to see what I learn there
  • lots of software work to do!

While I haven’t been able to resolve the stuff I don’t understand from the previous post I did manage to make some range tests. Yesterday my shopping trip took me 6mi out and my receiver, which uses a big Yagi was able to hear the little tag transmitting at 5dBm all the way. Without obstruction I got around -92dBm and with some tree obstructions down to ~104dBm and more sporadic.

Today’s outing got me 9mi away and holding the tag up with line of sight the RX was pretty consistent and again in the -102dBm range. Hard to believe…

I think that’s quite likely to be the limit. Need to tune TX and antenna and then try again.

Getting further away with line of sight is going to take some map reading and on-purpose outing…

3 Likes

Impressive!

1 Like

I got a little side-tracked and then finally vomited at stm32duino. Coincidentally a friend of mine has been tinkering on a nice modern C++ toolkit for smt32 with very nice simple tasks and threads. He tinkers 'til it’s perfect (never gets there :slight_smile: so I have to grab a version and just use it :-). It’s all down to bare metal: no LL, no HAL, no arduino, just grab the RefMan and go code!

After a couple of days of work I’m almost back to where I was with stm32duino: the radio stuff is all working (also from bare metal up) and I need to work out some of the low power sleep. Binary code size less than 1/4 of before and no more digging around in thousands of #ifdef to figure out what it’s actually really doing. Alright, enough ranting… :innocent:

With all this settled I can get to the interesting questions, like how to do error handling. For example, every interaction with the radio produces a status code to indicate whether the radio is happy or not. What should happen if the third command to the radio in a sequence of five to start transmitting returns an error while the bird is flying over the gulf enroute to mexico???

That could happen 'cause the battery chemistry has a bad day, or perhaps its a tad too cold, or some moisture crept in somewhere, or whatever. Or perhaps the gps is having a bad day and produces errors. What’s actually the point of even checking the status codes for errors?

I need all the error checks during dev, so I have to put the code in. Once it’s in, what should it do?

I’m against prod/dev switches/modes: I want the same code to run once it’s deployed and not some other variant that has different properties (size, timing). Although if no debug probe is connected it’s OK not to go through the motions of printing error messages.

The direction I’m leaning towards is to reset on error. With tiny rechargeable batteries and changing environmentals resets will occur: brown-out-reset, watchdog-reset, how-did-that-happen-reset. So I think I’ll try the following strategies:

  • save intermediate data in persistent data structures, meaning, in a way that survives a reset
  • skip data acquisition if there’s already recent saved data
  • turn any unexpected error into a reset

For example, an activity to get a GPS fix and then transmit it becomes 5 steps: check whether we have a recent fix, if not acquire a fix, save the fix, transmit the fix, delete the saved data. If the gps or radio signal an error: just reset. (well may want to tally some error stats in case these can be recovered later.)

It’s going to be some work to implement data structures (logs) that persist reliably across resets plus some that persist across power failures using flash but I need some of that regardless. Might as well lean in. Dunno how this will turn out…

I know that some of the most difficult scenarios are dealing with running out of power 'cause things can become intermittent and especially it can be very difficult to properly start-up again. It’s easy to land in an infinite loop where power comes on, activity starts and promptly exhausts power again, repeat.

Do you have experience with how to architect stuff like that? It’s always interesting to hear the battle stories of all the unexpected things that happen…

Well this is where the hard part of the firmware development start. Error handling is always the hardest thing to do right. And this is especially true for low power systems that are in the middle of nowhere. Sadly your thing is in this category, so you will need to put together some serious error handling. So what I would do in you place is first try to handle errors locally. So when you have an error in the radio, the radio driver should try to handle this internally. So reseting the rf, trying to configure it again, … and if the transmission is not up and running in a few retries, notify the main fw task/state machine that the connectivity is down and put the rf in to some low power state for some time and after that repeat the cycle… Something similar should be done for gps and other sensors. Most of the time errors occur because of the current location/device condition. For an example, bird could be hiding under a tree in a valley. Until the bird is there, you wont get a gps fix and likely event the rf signal. So by restarting the system you wont achieve anything except burning the battery… So avoid the system resets as much as possible, because they wont fix much :slight_smile:

Logging errors is a must, because this is the best way to check what you missed in error handling when you will get your trackers back. But writing persistent storage (flash) can be expensive regarding power consumption. Also you need to spread writing logs over all sector so you wont wear out just a part of the flash chip. So if you are not using standby mode, you can prebuffer or count errors in ram, maybe using a noinit section. It will retain its state even if you reboot the mcu without cutting of power :slight_smile: And then save to persistent storage when conditions are right or before your battery is about to die…

This was really generic answer, but is hard to be more specific considering having no deep knowledge about your tracker :slight_smile: So if you have any specific questions let us know :slight_smile: Sorry for the bad english. It is midnight here and when I am tired my english start to suck :smiley:

3 Likes

Izidor, thanks for your thoughts! I’ve been mulling over this for a while. The point about the energy consumed by a reset is a good one. That eliminates the system reset as a catch-all solution and more localized resets are required. In some cases that’s trivial, e.g. cycling power to the accelerometer and in others it isn’t, e.g. resetting the radio causes it to spend quite some energy in an initial calibration run.

I was also reading a memfault article where they argue that assert statements (which basically result in a system reset) should only be used to catch true software errors and not hardware malfunction. E.g., if some pointer is null and never should be then it’s reasonable to assert 'cause who knows what state the software got into. (there’s still an issue if this happens repeatedly.) But the code shouldn’t assert if the accelerometer is not responding the way it’s supposed to because chances are that there’s a HW problem and not a software one, so the software should continue running and have a way to handle the error.

So I think I’m left with 3 levels of errors (sigh):

  • clear software malfunction: reset, possibly disable some activity for a while if the same problem keeps recurring
  • hardware malfunction: reset/retry the hardware, skip the activity using that hardware for a while if the same problem keeps recurring
  • routine error condition (like not being able to get a fix): handle using retries as appropriate

Dealing with all this will require some thinking. It’s easy to overlook secondary failure modes… But it looks like I will need to keep track of which high-level activity the tag is performing so a repeated failure can suspend that ctivity for “a while” (prob exponentially increasing time-out). The top-level state machine may have that info.

I have yet to get to the logging… Past experience with writing a circular flash log module was… complex… way more than I had hoped. The stm32wle5 has 2K flash pages / erase blocks, so that’s good news. I haven’t looked at the power implications: haha.

Back to hacking accelerometer, barometer, and ADC drivers…