GNSS Base Station Testing

The previous post on the GNSS Base Station ended with a list of tests to run on the prototype. Multiple modifications were made (and plenty still need to be made) as a result of the testing, but the overall design remains very promising! The biggest problem left to fix is software reliability, but I think I have a good path forward to improve it.

Test Results

Power Budget

In order to measure both the power being generated by the solar panel and the power consumed by the circuits I used a USB power meter. If you plug a generating source in at once end and a consuming source at the other it will display the instantaneous power draw as well as integrate the total consumed electricity.

To measure consumption I plugged the electronics into a USB wall adapter to let them draw as much current as they wanted. At steady state (broadcasting NTRIP correction data over WiFi) the average power draw was only 0.18A. When the battery was charging I measured an increase of 0.5A in consumption, which matches the note on the datasheet. Additionally, when the ESP32 first boots up it draws an additional 0.08A for a few second, likely due to its attempt to connect to WiFi.

An unintended consequence of the increased current draw during bootup and while charging is that if the battery runs out over night then the unit will fail to boot when sun first shines on the panel in the morning. When the power coming from the panel first passes 0.18A the ESP32 boots, but then immediately demands 0.26A and browns out the solar panel. The under-voltage event causes the ESP32 to shut down, and then the cycle repeats until there is enough sunlight to sustain the boot sequence. Some people have fixed this by adding capacitors to just the right place to prevent the brownout, but I think just sizing the battery properly to avoid being powered off in the first place is a better fix.

To measure power production of the solar panel I used a USB power sink (basically just some power resistors) to let the solar panel try and max itself out. I was able to get 0.9A at 4.8V in directly sunlight, with about half of that when there were clouds, or the panel was not aligned with the sun. When only receiving indirect light (because it was in the shade of something else) the power production basically disappeared.

At 0.18A continuous consumption I need to generate 4.32Ah per day just to keep up. Because I can only take advantage of 0.68A of the available 0.9A at any given time (steady state draw with 500mA charging) I need about 6.5 hours of good sunlight each day to keep up with consumption.

This seems reasonable, but could be troublesome in the winter when there are technically only 10 hours of ‘daytime’ at my latitude. I can always program in some amount of ‘hibernation’ during the night when i don’t need GPS correction data to conserver battery.

WiFi Connectivity

I’ve had plenty of trouble getting past projects to connect to my home WiFi in areas where my cell phone has a great signal. I don’t know enough about the black art of antenna engineering to figure out what the best antenna should be for any given situation, so I just bought a bunch and tested them all!

The four types of antenna setups that I tested are:

To test them, I connected everything and logged the WiFi strength (RSSI in dB) as reported through the ESP32 API over the course of 5 minutes. All of the antennas were placed on the same spot on my desk, which is about two walls and 30 feet separated from my router. I expected the SMA antenna to blow the rest out of the water because of its large gain, despite a minor insertion loss from the extra connection, but I couldn’t have been more wrong. The results were:

  • Patch antenna: 51 dB
  • External U.FL antenna: 48 dB
  • External SMA antenna: 70 dB
  • Integrated ESP32 antenna: 61 dB

So it looks like my baseline of using the patch antenna is well worth the cost over the built in antenna, but upgrading to the external U.FL antenna might not be worth the waterproofing and mounting hassle.


The first thing to test was how porous the 3D printed plastic was. If the casing itself allows water through then I have no hope of waterproofing the entire device. I simply put the bottom half of the enclosure on a paper towel and filled it with water.

After leaving it overnight there was still tons of water in the enclosure and no signs of water on the paper towel. This is all the evidence I need to call the 3D printed part water tight!

To test the enclosure seals for water tightness I did my best approximation of an IPX water rating by spraying the sealed device in my shop sink.

I started by spraying from mostly vertical angles, switching it up every now and then, for about 5 minutes. Then I opened up the case to check for moisture. I didn’t see any, so I closed it up again and went back to the sink. This time I sprayed from plenty of horizontal angles and probably managed to get some water in at a joint where one of the two cables passes over an o-ring (instead of using a proper cable gland). When I opened the enclosure there were plenty of signs of water entry, even on the seal itself.

So I guess the enclosure is IP54 sealed? Good against vertical water (light rain) but not against strong horizontal water (heavy storms). The troublesome thing is that if water does get into the enclosure it has no way to get out! The next revision of the enclosure will probably implement a ‘tortuous path’ drain at one end to let any water out that might have gotten in.


Luckily I’m using a roof mount that was designed for much larger antennas than the one I am using, so I am not worried about stability. I did a test mount on the dock roof because it would be easy to observe there.

These types of mounts are meant to be ballasted to hold them down when the winds pick up. I grabbed a few cast concrete blocks from the hardware store and will throw them on for good measure.

Something I didn’t think about until I observed the antenna on the dock roof was how many animals might want to perch on top of the antenna and either block its signal or try to break it. There is a great blue heron that loves to sit (and poop) on the dock roof, but the main house roof is more frequently hosting smaller crows than herons. I didn’t catch anything sitting on the antenna during this test but it did make me wonder how I will get up on the roof to clean bird crap off of the antenna if it somehow starts to distort my signal.

Another thing I didn’t notice until after I was looking at the mounted device was that the antenna was shadowing the solar panel! I thought everything would be fine because that half of the mount faces north, but the antenna’s shadow couldn’t have been more perfectly disrupting. It will be easy enough to relocate the solar panel on the mount before the final install.


The single web page and API I built ended up being very usable. The only downside was that accessing it via IP address is kind of annoying. I should probably get a pi-hole on my network to handle (among other things) local DNS for all of my home devices.

When downloading a ubx file for surveying-in the base station the rest of the web page is unresponsive. I don’t really know a good way around that without diving really heavily into async web server stuff for the ESP32.


There were a lot of issues with the software I scrapped together for testing. While it generally worked, it was not good at recovering from failures in the I2C bus or the network connection to the NTRIP caster.

Connecting to RTCM server on port 2101
[E][WiFiClient.cpp:258] connect(): socket error on fd 55, errno: 113, "Software caused connection abort"
[E] Failed to initialize connection to ntrip server

This was my first time using free RTOS and I had chosen to create independent tasks for each feature (one for the web server, one for the GPS board, etc). In hindsight, it might have been smarter to create independent tasks for each resource instead of each feature (one for I2C comms, one for NTRIP client sending, one for web server, etc). That way tasks aren’t fighting to use the same resource all of the time, and if the resource does have an issue it is easy to pause all of its users and restart the resource without interference.


By following the instructions from the sparkfun tutorial I was able to connect a second ublox board to its own L2 antenna then plug it into my phone to use it as a GNSS client device. With my phone streaming in my correction data from RTK2GO I was able to achieve RTK float, and then a few minutes alter RTK fix with a reported accuracy of about 5mm! Of course that doesn’t include the inaccuracies of my base station survey, but combined I should still be able to get about 1cm of accuracy, which is plenty for the lawn mower project.

Changes Made

Data Logging

A constant issue I ran into was that I wanted to monitor the state of the device over time to help troubleshoot the issues I was having. While this isn’t a typical thing you should have to do if everything works well, it is invaluable if anything at all goes wrong. I ended up setting up an MQTT server on a local computer and used node-red to pipe the data received into a mysql database (because I already had one available) that I could read with grafana.

I log various metrics (like RSSI, number of satellites in view, fix type, bytes written to NTRIP) and messages (connection/disconnection events, error states) to tables in the database. The settings for the MQTT connection are available on the same simple web page as everything else.


The longest night at my latitude are only 14 hours long, but adding two hours on each end for twilight or weird shadowing is probably a good idea. At 0.18A of drain the battery needs to be 3.24Ah just to survive the night. Luckily I was able to find a 3.7mA battery on amazon with the right connector.

Or at least I thought it was the right connector.

When I plugged the battery into the ESP32 board I immediately smelled the magic smoke escaping! It turns out that the IoT industry and RC industry both use the same JST connector for small batteries, but wire it in opposite directions. I don’t know what exactly blew up on the board but it no longer charges batteries (it still runs on USB power just fine). Luckily I had a spare board so after swapping the wires in the battery connector everything was fine!


The enclosure needed to be redesigned to fit the bigger 3.7Ah battery. This just blows up the diameter a bit, which is not a big deal because it still fits on my printer.

I also wanted to include a set of ‘tortuous path’ drain holes on the downhill-sloped bottom edge of the lower enclosure. Basically this is a few very small zigzag holes that lets water drain out but prevent splashed water from entering. Figuring out the right size for the holes was tricky. I tested with 0.06″ diameter holes at first, but when water didn’t actually drain through them I upped it to 0.10″ and the water drained just fine.

Cross Section

One final change I made was to raise the battery and patch antenna of f the ‘floor’ of the enclosure so that any water that leaks in through a seal can run around the sensitive parts before it gets to the drain holes.

Changes to Make

Software Architecture

Like I mentioned previously, the majority of the issues right now are software related. I need to re-architect the system to segment tasks by the resource they interact with not by their functional role in the system.

While I’m making changes, I could probably do something to modularize the way I seem to be interacting with components. For example, all of my components have some settings that can be read/written from the web page and stored in static memory, but this listing and the associated web page features are manually synchronized. I bet there is a smart way for me to make some kind of ‘settings’ module/class that I can reuse for this and other projects.

Alternate Caster

I was able to successfully cast to the public RTK2GO NTRIP caster using the temporary password they give you when you fill out their registration. However, despite numerous emails to them I still have not received my permanent password. One of the many issues contributing to my systems instability is that the default password changes every once in a while and I get kicked off the server until I manually re-enter the password into my web page. I suppose I could auto-scrape the temp password from the RTK2Go website but that just seems to crazy to me.

There are other alternate free caster options out there. Emilid (which has probably the most affordable commercial L2 RTK base station and receiver on the market) has a free NTRIP caster service as well.


In order to make accessing all of these local network points (GPS station, MQTT servers) much easier for humans I should probably set up some kind of local DNS system on my home network. This will continue to come in handy as I add more connected devices in the future. A Pi-Hole seems like an easy way to do this, but I could just set up a more robust local server instead.

Mechanical Installation

It is a small change, but I need to relocate the solar panel on the roof mount so that it is not shadowed by the antenna. This might require a small 3D printed bracket or something to hold everything down securely.

While it may not be necessary, I also have some ballast to add to the corners of the roof mount just in case. They are simple, cheap, cast concrete blocks from the hardware store.

One Comment

  1. Nice update! 1cm is some crazy accuracy! Do you think that it’s every logged point that may come in at +-1cm, or is it just a constant offset from the ‘real world’ location? I guess repeatability to the same point in space is the question.
    As for water proofing did you consider epoxy blobs on the important bits? I imagine it would be nice to replace the battery but maybe there is a sweet connector you can buy.
    What material are you printing with? Winter/sun cycles might be hard on the material, it may warp/crack over time (maybe solid fill final product).
    Speaking of temp cycles- you may want to account for the discharge capacity at temp ranges, although I think lipos happy enough above 0C.

Leave a Reply

Your email address will not be published. Required fields are marked *