BoatBuddy
An open-source boat display cockpit for navigation, speed, heading, and tide tables running on Raspberry Pi and accessible as a webapp through any smartphone.
Features
- Navigational map showing current location, heading, location history, depths, buoys, and recommended track
- Speedometer showing current speed via GPS satellite data (toggle between Knots and MPH)
- Tide table showing high and low tides for next 8 periods
- Standalone access-point and self-hosted -- no internet connection required
- Accessible via any browser @ http://boatbuddy.live with ability for many simultaneous connections
- Bluetooth receiver to connect phones for playing music through Raspberry Pi audio output
![](https://github.com/arm358/BoatBuddy/raw/main/screenshots/IMG_5588.jpg)
Parts List
- Raspberry Pi 3b+ || Raspberry Pi 4 || Raspberry Pi Zero 2
- Any of these will work. Any Raspberry Pi less powerful is not recommended due to the overhead of the data smoothing algorithm
- If you choose a Raspberry Pi Zero 2, it will work but the Bluetooth audio function will not since it does not have a 3.5mm Audio Output
- Adafruit GPS Breakout
- Small wires, solder, soldering iron, etc. to turn the GPS Breakout into a HAT.
Installation
Option 1: BoatBuddy Image
This is the easier option and provides all features out of the box. At this Google Drive link is a disk image of BoatBuddy (too large to host on GitHub). Unzip the file and flash it onto an SD card of sufficient size using the program of your choice (I like Etcher). Once flashed, proceed to the Setup section.
Option 2: The Hard Way.
-
Get the repo: Clone the repo to your home directory on your Raspberry Pi (structure should look like
/home/pi/BoatBuddy
) -
Enable the server & data script on startup: Edit the startup file
/etc/rc.local
(sudo nano /etc/rc.local
) and add these lines before the lineexit 0
to start the webserver and the data collection script on startup.cd home/pi/BoatBuddy python3 manage.py runserver boatbuddy.live:80 & python3 data.py &
-
Get the maptiles for hosting the map offline: Download the zip file containing all of the maptiles (also too large to host on Github). This is required to host the map offline. Unzip the
ne.zip
file intoboatbuddy/core/static/core
(440k+ individual .pbf files!). The full path on the Raspberry Pi should look like/home/pi/boatbuddy/core/static/core/ne/...
-
Install Python dependencies: Install the requirements from the
requirements.txt
file to the global Python libraries:sudo pip3 install -r requirements.txt
- You must run using sudo because the startup script
/etc/rc.local
runs as root on startup. - You may get lots of errors here. Hard to predict. Work through them one at a time, and raise an Issue if you need help.
- You must run using sudo because the startup script
-
Set RPi up as Access Point: Follow this guide through step 5, excluding step 6 and beyond, to set up the Raspberry Pi as an access point. We do not want to set up forwarding of the wifi traffic to the ethernet port.
-
Add DNS entry for URL: Edit the file
/etc/hosts
(sudo nano /etc/hosts
) and add the following line at the bottom. Replace the IP address with whatever you set as thestatic ip_address
when setting up the access point in thedhcpcd.conf
file. If you followed the steps in the guide exactly, it should be192.168.0.10
:192.168.0.10 boatbuddy.live
Note: Bluetooth setup is outside the scope of this installation whereas it is included in the BoatBuddy image (Installation Option 1). It requires an older version of Raspberry Pi OS and an install script from a different Github repo. This can be configured later by the user.
Setup
If installed via the flashed image (Installation Option 1):
ssh username = pi
ssh password = raspberry
wifi name = BoatBuddy
wifi password = boatbuddy
If installed manually (Installation Option 2):
Passwords and wifi name could be different. Hope you remembered what you set them to.
Connecting GPS Module
Wire the GPS module as follows:
Usage
Connection & Viewing
- Connect to the BoatBuddy WiFi network and enter password if necessary
- Navigate to http://boatbuddy.live
- In order to remove address bar on Apple iOS devices for a "cleaner" look:
- In Safari and while on the page:
- Tap the "Share" button on the bottom bar
- Tap "Add to Home Screen"
- Tap "Add"
- The page should now have a shortcut on your homescreen, and will display in an app-like format (without the address bar at the top)
- In Safari and while on the page:
Functions
Note: all functions work in portrait and landscape mode, but landscape fits the screen better
-
Tap the Tide Data icon in the top-left corner to toggle the tide chart
- The Tide data button is replaced by the current speed.
- Tap again to revert back to Speedometer
-
Tap anywhere on the Speedometer chart to change to MPH instead of knots.
- Tap again to revert back to knots
-
Tap the Heading icon in the top right corner to toggle to the Map.
- Use pinching and two-fingers to zoom and spin the view.
- Tap the Lock button to stop the view from being moved to the current position. Useful for panning around the map.
- Tap the Unlock button to re-couple the view with the current position.
- Tap the Route button to toggle on/off the track history (previous route) from view.
- Tap the Heading again to revert back to the Speedometer (or tide chart -- whichever was displayed last)
-
Tap the Power button and confirm to safely shutdown the system
Configuration
The current configuration is for Ocean City, NJ. If you live or boat there, great! You're good to go. To reconfigure for other cities:
Changing Tide Data for your location
- Open
/boatbuddy/Tide Scraping Script/tide_scraper.py
and change the URL and Date variables to your location and desired date range (URL is for api.tidesandcurrents.noaa.gov) - Save the output file
tides.csv
into the main /boatbuddy folder replacing the existing file.
Changing the default boat location and Home icon location
- Open the
views.py
file in/boatbuddy/core/
- In the home view, add the coordinates in list form
- Example
[-75.778, 39.332]
- Example
Adding depth soundings, buoys, or other data to the map for a different location:
- Navigate to the ENC Data from NOAA and download the charts that cover the area you need.
- Use the ENC chart locator tool to determine which charts you need.
- Unzip the downloaded file and upload the spatial data file with file extension .000 to MyGeoData Cloud Converter
- Example for
US3SC10M
Spatial Data File would be located inENC_ROOT/US3SC10M/US3SC10M.000
- Example for
- Select the output format as GEOJSON and convert your data.
- Open the
geoconverter.py
file in/boatbuddy/geojson_formatter/
. Change theoutput_name
to any value ending in .json andinput_name
to the file in which you would like to convert.- NOTE - you may have to change the for-loop dictionary keys to fit the data. It's currently set up to read the
SOUNDG.geojson
file but for other files it will be necessary to tweak. - To see a list of the Acronym -> Full Name mappings, visit http://www.s-57.com (for example, depth soundings are contained in the
SOUNDG.geojson
file) - This will convert the GEOJSON data to a slightly different format that is required for MapLibre to display.
- NOTE - you may have to change the for-loop dictionary keys to fit the data. It's currently set up to read the
- Save the output file to
/boatbuddy/core/static/core/
- Open the
index.html
file at/boatbuddy/core/templates/
and scroll to the MapLibre Javascript section (starting around line 185) - Copy a
map.addSource(...
section up until the semicolon and add it to the list of othermap.addSource(...
sections. - Change the name to something unique and change the
"data":
property to refer to your new filemap.addSource("US3SC10M_SOUNDG", { "type": 'geojson', "data": "{% static 'core/USD3SC10M_SOUNDG.json' %}" });
- Copy a
map.addLayer(...
section up until the smicolon and add it to the list of othermap.addLayer(...
sections. - Change the
"id"
and"source"
properties to refer back to the source created in step 9, change the"type"
property to the correct type (depth soundings would be symbols, tracks would be lines, buoys would be symbols etc.) and change the rest of the properties that correspond with your type of layer.map.addLayer({ 'id': 'US3SC10M_SOUNDG', 'type': 'symbol', 'source': 'US3SC10M_SOUNDG', layout: { 'text-field': "{depth}", 'text-font': ["Open Sans Regular"] } });
Note - other types of layers (for lines or for symbols with a picture instead of "text-field" will have different properties in the map.addLayer(...
function. Refer to the other layers already present on the map or visit the Mapbox documentation (which is the same syntax for MapLibre) for more details.
How It Works
Various technologies, open source repos, and free data were used on this project:
- Django as back-end framework for HTTP server
- Django-Channels for sending GPS data to front-end clients via websockets
- MapLibre for displaying interactive map in Javascript (open source fork of Mapbox)
- Chart.js for tide chart and speedometer gauge display
- Bulma CSS framework for styling
- NOAA ENC data in GeoJSON format for depths, buoy locations, etc.
- Various open-source Python repos used in websocket script for collecting, manipulating, and sending data
Issues / Troubleshooting / FAQ
- The site loads but there is no data being sent.
- Add this line
exec 1>/tmp/rclocal.out 2>&1
to the top of the/etc/rc.local
file. This will make a log in/tmp/rclocal.out
where you can investigate the issue futher.
- Add this line
- Map loads but doesn't have any detail on it (city names, streets, etc.).
- Because of space limitations and the large number of files used by the map tiles at different zoom levels, the map only has detailed information for the North East. Anywhere else (such as Florida or West Coast) will display but not have detailed information. To change this is outside of the scope of this document. Please raise an Issue if you need help setting up a different area of the country.
- Why are you running the default Django http server and not Apache/NGINX/Daphne?
- A dedicated HTTP server would add an additional layer of abstraction here and additional setup for no gain. Further, the site is "airgapped" and has no connection to the outside world. The security risks here are miniscule. The default Django http server works well in this instance without any further setup required.