Sunday, September 27, 2015

How to Make an NMEA-0183 Tilt-Compensated Compass and Rate-Of-Turn Indicator

This simple, analogue compass costs $10. If we want to use this for electronic chart-plotting applications, then we need an electronic compass that takes your magnetic heading and converts it into a digital signal. This compass does that, for the low, low price of $774.95. Why does an electronic compass, based on technology over four thousand years old, cost so much? This guide will show you how to build your own for under $100.

Recommended Gear

Now Why Is a Marine Compass so Expensive?

Aside from the obvious "it has the word 'marine' in the title, so it costs X times more," there's a valid reason it's more expensive than just a regular electronic compass. Any compass application for a moving vehicle needs to be securely mounted such that magnetic north aligns exactly with the vehicle longitudinal axis. In more basic terms, it needs to be completely flat and lined up with forward. When you take an analogue compass (like I linked to in the first paragraph) and tilt it just a little bit, the needle quickly jams on its pivot and it becomes unusable. So, in a rocking, rolling, listing sailboat, you need a tilt-compensated compass.

One way to do this is to put the compass on a two-axis gimbal (kind of like how some stoves are). This way, as the boat rolls and pitches, it remains flat relative to gravity, and will continue to work just fine. However, that requires you to build a two-axis gimbal, which isn't exactly a quick job if you want to do it correctly.

Fortunately, there's an easier way. Just get three compasses, and line them up so that one is flat against each axis. Lay one done so it's flat with respect to the horizon (Z), another with respect to the boat's longitude (X), and another with respect to the boat's latitude (Y). Then you just filter each compass's output through a complicated formula, and you get your magnetic heading. Fortunately, the MPU-9150 has taken care of all that hard work.

This guide assumes you've followed my previous post and set up the versatile MPU-9150 9-Axis Accelerometer, Gyro, and Compass. It should be fully working, and putting out valid data as demonstrated at the end of that post. If you have problems, comment at the bottom and I'll respond as soon as I can.

It also assumes you have gone through my other post and you have a good understanding of how to create an NMEA-0183 sentence with an Arduino. In fact, if you've gone through both of those posts and understand them, this is a very simple and quick job.

The Sketch for NMEA-0183 True Heading

 ////////////////////////////////////////////////////////////////////////////  
 //  
 // This file is part of RTIMULib-Arduino  
 //  
 // Copyright (c) 2014-2015, richards-tech  
 //  
 // Permission is hereby granted, free of charge, to any person obtaining a copy of   
 // this software and associated documentation files (the "Software"), to deal in   
 // the Software without restriction, including without limitation the rights to use,   
 // copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the   
 // Software, and to permit persons to whom the Software is furnished to do so,   
 // subject to the following conditions:  
 //  
 // The above copyright notice and this permission notice shall be included in all   
 // copies or substantial portions of the Software.  
 //  
 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,   
 // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A   
 // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT   
 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION   
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE   
 // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  
 //  
 // Further modified by King Tide Sailing (kingtidesailing.blogspot.com)  
 //  
 ////////////////////////////////////////////////////////////////////////////  
   
 #include <Wire.h>  
 #include "I2Cdev.h"  
 #include "RTIMUSettings.h"  
 #include "RTIMU.h"  
 #include "RTFusionRTQF.h"   
 #include "CalLib.h"  
 #include <EEPROM.h>  
 #include <PString.h> 
   
 RTIMU *imu;               // the IMU object  
 RTFusionRTQF fusion;          // the fusion object  
 RTIMUSettings settings;         // the settings object  
  
 // User defined variables (for your own boat and geographic location):  
 float magd = -14.0;     // magnetic deviation; if it's East, it's negative (14E in San Francisco 2015)  
   
 // if you don't want to include a sentence, then just make the appropriate variable false  
 // it is recommended that you print only the minimum sentences desired to keep the Serial Port from getting too busy  
 boolean outputhdm = false; // magnetic heading  
 boolean outputhdt = true;  // true heading  
 boolean outputrot = true;  // rate of turn  
 boolean outputshr = true;  // pitch and roll  
   
 // Variables preset with recommended values (but can be changed):  
 #define DISPLAY_INTERVAL 500  // rate (in milliseconds) in which MPU9150 results are displayed  
   
 // Global variables:  
 unsigned long lastDisplay;  
 unsigned long lastRate;  
 int sampleCount;  
 float r = M_PI/180.0f; // degrees to radians  
 float d = 180.0f/M_PI; // radians to degrees  
   
 void setup()  
 {  
   int errcode;  
   
   Serial.begin(4800);   // output port to computer  
   Wire.begin();  
   imu = RTIMU::createIMU(&settings);  
   
   // Checking the IMU, if it fails, simply restarting the connection seems to work sometimes  
   if ((errcode = imu->IMUInit()) < 0) {  
     Serial.print("Failed to init IMU: "); Serial.println(errcode);  
   }  
   if (imu->getCalibrationValid())  
     Serial.println("Using compass calibration");  
   else  
     Serial.println("No valid compass calibration data");  
   
   lastDisplay = lastRate = millis();  
   sampleCount = 0;  
   
   // Slerp power controls the fusion and can be between 0 and 1  
   // 0 means that only gyros are used, 1 means that only accels/compass are used  
   // In-between gives the fusion mix. 0.02 recommended.  
     
   fusion.setSlerpPower(0.02);  
     
   // use of sensors in the fusion algorithm can be controlled here  
   // change any of these to false to disable that sensor  
     
   fusion.setGyroEnable(true);  
   fusion.setAccelEnable(true);  
   fusion.setCompassEnable(true);  
 }  
   
 // calculate checksum function (thanks to https://mechinations.wordpress.com)  
 byte checksum(char* str)   
 {  
   byte cs = 0;   
   for (unsigned int n = 1; n < strlen(str) - 1; n++)   
   {  
     cs ^= str[n];  
   }  
   return cs;  
 }  
   
 void loop()  
 {   
   unsigned long now = millis();  
   unsigned long delta;  
   int loopCount = 1;  
    
   while (imu->IMURead()) {  
     if (++loopCount >= 10)  
       continue;  
     fusion.newIMUData(imu->getGyro(), imu->getAccel(), imu->getCompass(), imu->getTimestamp());  
     sampleCount++;  
     if ((delta = now - lastRate) >= 1000) {      
       sampleCount = 0;  
       lastRate = now;  
     }  
     if ((now - lastDisplay) >= DISPLAY_INTERVAL) {  
       lastDisplay = now;  
       RTVector3 pose = fusion.getFusionPose();  
       RTVector3 gyro = imu->getGyro();  
       float roll = pose.y()*-1*d;     // negative is left roll  
       float pitch = pose.x()*d;       // negative is nose down  
       float yaw = pose.z()*d;         // negative is to the left of 270 magnetic  
       float rot = gyro.z()*d;         // negative is to left  
       float hdm = yaw-90;             // 0 yaw = 270 magnetic; converts to mag degrees  
       if (yaw < 90 && yaw >= -179.99) {  
        hdm = yaw+270;  
       }  
       float hdt = hdm-magd;           // calculate true heading  
       if (hdt > 360) {  
         hdt = hdt-360;  
       }  
       if (hdt < 0.0) {  
         hdt = hdt+360;  
       }  
   
       // HDM Magnetic Heading //  
       if (outputhdm = true){  
       char hdmSentence [23];   
       byte csm;  
       PString strm(hdmSentence, sizeof(hdmSentence));  
       strm.print("$HCHDM,");  
       strm.print(lround(hdm));  // lround simply rounds out the decimal, since a single degree is fine enough of a resolution
       strm.print(",M*");   
       csm = checksum(hdmSentence);  
       if (csm < 0x10) strm.print('0');  
       strm.print(csm, HEX);  
       Serial.println(hdmSentence);  
       }  
   
       if (outputhdt = true){  
       // HDT True Heading //  
       char hdtSentence [23];   
       byte cst;  
       PString strt(hdtSentence, sizeof(hdtSentence));  
       strt.print("$HCHDT,");  
       strt.print(lround(hdt));  
       strt.print(",T*");   
       cst = checksum(hdtSentence);  
       if (cst < 0x10) strt.print('0');  
       strt.print(cst, HEX);  
       Serial.println(hdtSentence);  
       }  
   
       if (outputrot = true){  
       // ROT Rate of Turn //  
       char rotSentence [18];   
       byte csr;  
       PString strr(rotSentence, sizeof(rotSentence));  
       strr.print("$TIROT,");  
       strr.print(lround(rot)*60);    // multiply by 60, since ROT is measured in degrees per minute
       strr.print(",A*");   
       csr = checksum(rotSentence);  
       if (csr < 0x10) strr.print('0');  
       strr.print(csr, HEX);  
       Serial.println(rotSentence);  
       }  
   
       if (outputshr = true){  
       // SHR Pitch and Roll (no heave... yet...)//  
       char shrSentence [50];   
       byte csp;  
       PString strp(shrSentence, sizeof(shrSentence));  
       strp.print("$INSHR,,");  
       strp.print(lround(hdt));  
       strp.print(",T,");  
       strp.print(lround(roll));  
       strp.print(",");  
       strp.print(lround(pitch));  
       strp.print(",,,,,1,0*");  
       csp = checksum(shrSentence);  
       if (csp < 0x10) strp.print('0');  
       strp.print(csp, HEX);  
       Serial.println(shrSentence);  
       }  
      }  
    }  
 }  

So what's going on here? As long as you mount the MPU-9150 completely flat with respect to the boat (parallel with the water line, forward pointing towards the bow), then it will give you valid heading data. You must adjust a few variables at the top, mainly the magnetic deviation for where you're boat is. If you're having trouble finding that, it may help to look up nearby airports, since magnetic deviation is important for aviation.

The variables just below that dictate what gets printed. For the most part, marine applications use true heading (as do aviation applications), but if your chart plotter incorporates a geodetic magnetic deviation model, then you may just want to print the magnetic sentence.

I also have the lround function in there, which gets rid of the decimal place for the headings. A single degree is fine enough of a resolution (101.5 degrees means the same to me as 102 degrees). If you wish to keep the decimals, then just remove the lround functions.

The HDM and HDT sentences are self-explanatory enough, but what about ROT? Simple: it's Rate of Turn, how many degrees per second your boat is turning. And SHR? That's the roll and pitch of the boat, which you can end up doing some pretty cool things with if you know how to use them.

That's really all there is to say about this. It's a simple sketch, and it's with some pretty low cost parts. In fact, if you tally it up, you get a tilt-compensated compass for about ~$100. Now that's more like 2015.

How to Connect any NMEA-0183 Device to an Arduino


NMEA-0183 uses 12V signals to communicate with each other. The oldest instruments use RS-232, or a single wire that sends a high voltage followed by a low voltage, to indicate on or off. Newer NMEA-0183 devices us the RS-422 protocol, which uses two wires (a positive and a negative) that alternate between high and low to achieve the same effect, with greater accuracy and less chance for error. But the point is, both use 12 volts along their wires, and that will fry your Arduino if you plug it directly in.

EDIT 24 May, 2016: Apparently this is incorrect. 5V is the industry standard for RS-422, and 10V is the max voltage allowed for open circuits. See Max44's comments below the entry for more.

Recommended Gear

NMEA-0183: RS-232 or RS-422 or RS-485?

This part is really easy. If you have a single transmit wire from your device (most likely labeled TX or NMEA OUT or something like that), then it uses the RS-232 protocol, and you need the respective converter. If your device has two wires coming out (typically labeled NMEA OUT+ and NMEA OUT- or TX+ or TX-), then you have RS-422. What about RS-485? For our purposes, it's the same thing as RS-422. The difference is that with 485, you can connect a bunch of different devices that can all talk to each other, which is cool and all, but not applicable for our NMEA device. For our purposes, treat RS-485 the same as RS-422, since the protocal is identical (just RS-485 can support a lot more devices than RS-422, like on the order of 80 or so more).

Wiring Diagrams for NMEA-0183 and Arduino devices

There are way too many converters out there to cover them all, but I'll cover a few here. You are basically looking at a simple system: the NMEA-0183 device sends a signal to the converter (either a single wire or two), and that tones the signal down to 3-5V for the Arduino, and pipes that signal out to the input on the Arduino itself. For the most part, you don't need power since most NMEA-1083 devices require 12V, something the Arduino can't put out. So here are a few examples:

SparkFun MAX3232 Wiring Diagram (for RS-232)

NMEA-0183MAX3232ARDUINO
TXT1 OUT

R1 OUTReceive Pin

SparkFun RS232 Shifter (No DB9) (for RS-232)

NMEA-0183RS-232 ShifterARDUINO
TXPin 2 (right next to the square hole)
TX-OReceive Pin

RS-422/485 Shifter

NMEA-0183RS-422/485 ShifterARDUINO
NMEA+B
NMEA-A
VCC5V
GNDGround
REGround
ROReceive Pin

It also goes without saying that you need to ensure your device is powered through the boat's electrical system. Now that we have our device physically connected, let's work through the code.

My super hi-tech test bed

The NMEA-0183 Arduino Library

Marten Laamer's has done a fantastic job building this useful and lightweight library. You'll need to download his library and check out his website over here first. There are hundreds of tutorials out there to get a GPS working (including some that come with the library), which is useful but pretty easy to figure out, so I'm going to show how to use his library to parse any NMEA sentence.

First, you must open up nmea.h and change this line

 #include "WConstants.h"  

to this

 #include "arduino.h"  

Then open up nmea.cpp and change this line

 #include "WProgram.h"  

to this

 #include "arduino.h"  

The most basic application is to receive an NMEA sentence, and then send it without doing anything. Here's a quick sketch for doing just that (remember, you must have the NMEA library included).

 #include <nmea.h>   
     
  NMEA nmeaDecoder(ALL);   
     
  void setup() {   
  Serial.begin(4800);   
  Serial2.begin(4800);   
  }   
     
  void loop() {   
  if (Serial2.available()) {                 // if something is incoming through the Serial Port
   if (nmeaDecoder.decode(Serial2.read())) { // if it's a valid NMEA sentence   
    Serial.println(nmeaDecoder.sentence());  // print it   
    }   
   }   
  }  

Pretty boring and basic. Here's what the Serial Monitor output looks like. So if you connect this to your chart plotter, this is what it will see. If you connect more than one instrument, then just include each "If Serial is Available" loop for each appropriate Serial Port.

If you have no clue what's going on, this basically scans the Serial port that we plugged our converter into (the Receiver Pin in the wiring diagrams above). We're using Serial Port 2 as our input for my DST-800, which is using the RS-422 converter. If you are using an RS-232 device (with only one NMEA Transmit wire), then you actually have to get a little more fancy because the converter inverts your signal. So if you are using an RS-422 device, and you're not getting good output, then maybe just swap the A and B wires and try again.

But you can't just do that with an RS-232 signal, since there's only one wire. So what you have to do is use the Software Serial Library. Here's a sample sketch that uses an RS-232 NMEA-0183 device.

 #include <SoftwareSerial.h>  
 #include <nmea.h>  
   
 SoftwareSerial nmeaSerial(10,11,true); // RX pin, TX pin (not used), and true means we invert the signal  
 NMEA nmeaDecoder(ALL);   
   
 void setup()  
 {  
  Serial.begin(4800);  
  nmeaSerial.begin(4800);  
 }  
   
 void loop()  
 {  
   if (nmeaSerial.available() > 0 ) {  
     if (nmeaDecoder.decode(nmeaSerial.read())) {  
     Serial.println(nmeaDecoder.sentence());  
    }  
   }
 }  

Note that this includes the SoftwareSerial Library. This is basically taking a Serial signal, and instead of plugging it into a Serial Port, we're plugging it into a digital pin and simulating a Serial Port. We have to do it this way because this is the only way to invert the Serial Signal on an Arduino. I suppose you could get another converter, and wire two of them together to invert the signal twice (ending with the original signal), but that's wholly unnecessary.

Something else to note with this is that I tried hooking up my wireless wind vane to it, but the signal kept getting garbled. Unfortunately, I don't know if it was the converter not doing it's job, or if it was the unit itself. It occasionally spat out a wind sentence, but it wasn't usable because it was one constant stream of meaningless characters interspersed with a valid sentence. I ended up returning the wind vane, and hopefully the next one works fine. But it DID put out valid sentences for the first week; so I know it works with the Max3232 converter.

But now let's get a little more complicated. Here's a sample sketch that parses the sentence, and allows you do manipulate it how you want. Then we reconstruct it as a valid sentence, including a checksum function. A huge thanks goes to Tom over at https://mechinations.wordpress.com/ for guiding me in the right direction for the sentence creation on an Arduino. I highly recommend you go over there to check out his cool stuff (especially this post).

 #include <nmea.h>  
   
 NMEA nmeaDecoder(ALL);  
   
 void setup() {  
  Serial.begin(4800);  
  Serial2.begin(4800);  
 }  
   
 void loop() {  
 if (Serial2.available()) {  
   if (nmeaDecoder.decode(Serial2.read())) {  // if we get a valid NMEA sentence  
     Serial.println(nmeaDecoder.sentence());  
     char* t0 = nmeaDecoder.term(0);  
     char* t1 = nmeaDecoder.term(1);  
     char* t2 = nmeaDecoder.term(2);  
     char* t3 = nmeaDecoder.term(3);  
     char* t4 = nmeaDecoder.term(4);  
     char* t5 = nmeaDecoder.term(5);  
     char* t6 = nmeaDecoder.term(6);  
     char* t7 = nmeaDecoder.term(7);  
     char* t8 = nmeaDecoder.term(8);  
     char* t9 = nmeaDecoder.term(9);  
     Serial.print("Term 0: ");  
     Serial.println(t0);  
     Serial.print("Term 1: ");  
     Serial.println(t1);  
     Serial.print("Term 2: ");  
     Serial.println(t2);  
     Serial.print("Term 3: ");  
     Serial.println(t3);  
     Serial.print("Term 4: ");  
     Serial.println(t4);  
     Serial.print("Term 5: ");  
     Serial.println(t5);  
     Serial.print("Term 6: ");  
     Serial.println(t6);  
     Serial.print("Term 7: ");  
     Serial.println(t7);  
     Serial.print("Term 8: ");  
     Serial.println(t8);  
     Serial.print("Term 9: ");  
     Serial.println(t9);  
     Serial.println("--------");  
     }  
   }  
 }  

And here's the output:


As you can see, we can extract different terms from the sentence quite easily, and manipulate them if need be (but be careful--if you don't redeclare each term, then it will reflect the previous sentence if the new sentence doesn't have one). For example, my DST-800 has a temperature sensor on it which gives out the YXMTW NMEA sentence, which is the Mean Temperature of the Water (the first two characters, "YX," are largely meaningless for NMEA-0183--they just designate the name of the device transmitting. You could put anything there and it would still work fine). A good place to start for NMEA sentences is this page here, and we see that the MTW sentence format is as follows:

          1  2  3   
          |  |  |   
  $--MTW,x.x,C*hh  
 1. Temperature  
 2. Units  
 3. Checksum  
In order to print our own NMEA sentence, we will also need the PString Library available over here. Let's go ahead and change that Fahrenheit, with the following sketch:

 #include <PString.h>  
 #include <nmea.h>  
   
 NMEA nmeaDecoder(ALL);  
   
 void setup() {  
  Serial.begin(4800);  
  Serial2.begin(4800);  
 }  
   
 // calculate checksum function (thanks to https://mechinations.wordpress.com)  
 byte checksum(char* str)   
 {  
   byte cs = 0;   
   for (unsigned int n = 1; n < strlen(str) - 1; n++)   
   {  
     cs ^= str[n];  
   }  
   return cs;  
 }  
   
 void loop() {  
 if (Serial2.available()) {  
   if (nmeaDecoder.decode(Serial2.read())) {  
     char* title = nmeaDecoder.term(0);  
     if (strcmp(title,"YXMTW") == 0) {            // only run the following code if the incoming sentence is MTW  
      Serial.println(nmeaDecoder.sentence());     // prints the original in Celsius  
      float degc = atof(nmeaDecoder.term(1));     // declares a float from a string  
      float degf = degc*1.8+32;                   // and converts it to F  
       
      // Time to assemble the sentence  
      char mtwSentence [18];                      // the MTW sentence can be up to 18 characters long  
      byte cst;  
      PString strt(mtwSentence, sizeof(mtwSentence));  
      strt.print("$YXMTW,");  
      strt.print(degf);  
      strt.print(",F*");  
      cst = checksum(mtwSentence);  
      if (cst < 0x10) strt.print('0');            // Arduino prints 0x007 as 7, 0x02B as 2B, so we add it now  
      strt.print(cst, HEX);  
      Serial.println(mtwSentence);  
     }  
    }  
   }  
 }  

And this is the output:


And there it is! We have parsed the sentence, converted it to Fahrenheit, and then printed out a new NMEA sentence. Pretty simple, right?

Final Thoughts on NMEA-0183 Devices and Arduino

This means we can do a lot of cool things, such as: convert our speed to miles per hour, or extract our latitude/longitude for any sort of application. Or even filter our wind speed using the MPU-9150. Or anything, really, and then make our own sentence with whatever data we want. This unlocks a huge amount of possibilities for chart plotting, sailing, and taking charge of your own sailboat.


Saturday, September 26, 2015

How to Setup the MPU-9150 9-Axis Accelerometer, Gyro, & Compass with an Arduino


The MPU-9150 9-Axis Accelerometer, Gyro, & Compass (hereafter referred to as just "The MPU") gives you a lot of info that can be used in some very creative ways. But for my purposes, I'm only looking for a few key points of data; namely, the magnetic heading of the sensor, its roll/pitch/yaw, and the rate at which it is rolling/pitching/yawing. So I'm going to explain how to set this nifty little chip up, calibrate it (which is absolutely necessary!), and declare floating-point variables for use later on. As with the spirit of King Tide Sailing, this is written for the person who has no clue what they're doing--so don't worry if you're like me and aren't a programmer during the day.

Recommended Gear

Setting Up the Arduino

This part is pretty simple. Head over to the Arduino Getting Started page and get familiar with the Arduino. In a nutshell, you download their IDE program, write your code in said program, upload it to the Arduino, and then use the program's Serial Monitor to make sure it's working correctly. But since we wan't to do more than just make the little LED light blink (which is a good starter program, I think), we have to include Libraries that tell the Arduino how to interface with other devices... such as the MPU.

Problem is, it's really difficult to find a good Library for the MPU that spits out the information I need, but doesn't require a separate computer program to decode in the information. It all has to be contained in the Arduino, since this is going on my sailboat and it won't be hooked up to my laptop. The reason why this is a particularly important issue to resolve is because the MPU is pretty worthless without calibration, and the calibration data needs to be saved on the Arduino itself. Most Libraries and calibrators save the data on your end machine to filter the output before you do anything. Like I said before, that's not going to work for our application.

Fortunately, there is a library out there that does exactly that. It's Richard Tech's RTIMULib-Arduino, specifically for the Arduino, available at GitHub. This is a pretty simple library that saves the compass calibration data to the Arduino's EEPROM, or it's "read-only but writable memory." But if you go to the GitHub page, you'll see that in big bold letters it says, "Please note that this library is no longer supported." Don't panic. That's totally fine. And just in case something happens over there, I've uploaded the library here, which is the exact copy I'm using. Please note, however, that you should always go to the GitHub page for the most recent software and that there's no guarantee mine will work with updates.


So go over to the RTIMULib-Arduino GitHub page and click the "Download Zip" button on the right side (or just download the zip above). Unzip and copy its contents over to your Arduino folder such that it looks like the screenshot here. Next, open up the Arduino IDE, open up the "sketch" (which is the fancy term for program) titled "ArduinoMagCal.ino" which is in the ArduinoMagCal folder. Ensure your Arduino is connected to your laptop (and you should have already made sure you can upload code when you went through the starter guide at the Arduino's website), and click the right arrow at the top to upload the sketch to the Arduino.

Great, now the code is loaded onto the Arduino--we just have to connect the MPU itself for it to work.

Connecting the MPU-9150 to the Arduino

Plugin the soldering iron, we're about to get down and dirty. You are basically looking at only four wires: power, ground, SDA, and SCL. Ideally, you should already have this mounted in your box or wherever it will be for use, so that it is calibrated in exactly the same spot it will be used. But for this guide, I'm just going to do it at my dining room table. Here's the wiring guide:

Arduino MPU-9150
3.3V VCC
Ground Ground
SDA SDA
SCL SCL

Pretty simple, right? Using the male end of a jumper cable, plug each wire into it's respective pin on the Arduino. Now, you have to be very careful with this next step so you don't cross solder the pin headers. I strongly recommend you break off four header such that they stay connected to each other, and then solder them into the four pins on the MPU. They're all right next to each other, so that part works out just fine. But now you have to plan ahead a little bit--my MPU is actually mounted on the underside of the lid of my box. The MPU's orientation is printed on the board itself, so make sure it's oriented such that the Mag X is pointed to the bow of the boat (please note that it is different than the gyro and accelerometer orientation). Anyways, what this means is that I actually had to solder the pins such that the male prong sticks out the bottom of the chip (since it will be hanging that way when the lid of the box is closed). Confused? Just take a look at the pics to see my setup (ignore the Raspberry Pi and other gadgets).



How do you solder such small connections without spilling solder over to other pins? Very carefully. Also, use the little stand that came with the iron to hold it steady! After you solder the pin headers, connect the female end of the jumper cables to their respective pins. And now it's ready to go!

Calibrating the MPU-9150

We've already loaded the code, the MPU is now connected--so all it's waiting for is to be initiated. To do this, open the Serial Monitor in the Arduino IDE (it's under Tools), and you should see something like the screen shot here. If you don't, here's a few pointers:
  • Ensure your baud rate is correct. The ArduinoMagCal sketch defines the baud rate as 115200, so make sure this matches what the Serial Monitor is looking for using the drop-down menu on the bottom right.
  • Close the Serial Monitor, and open it back up. This "re-initiates" the Arduino.
  • Unplug the Arduino, wait a few seconds for it to fully power down, then plug it back in and repeat.
Great. Now it's spitting out its min and max values for each compass. To calibrate it, simply spin the MPU along each axis, one at a time. So, pitch it completely forward so that it spins back over front 360 degrees. Then, roll it left to right all the way through another 360 degrees. Finally, rotate it through every magnetic heading--all 360 of them. The whole time, it'll be logging the min/max values, which it uses for correction. When you've cycled through each axis, type in the letter "s" in the text box at the top, click Send, and it will tell you "Mag cal data saved for device MPU-9150." And that's it! Your MPU-9150 is now calibrated!

Using the MPU-9150 with Arduino

Now we're ready to start using the MPU. Here is a sample sketch I made using the RTIMULib-Arduino sketch, but modified so as to declare variables for each parameter. You can download it right here, or just copy and paste it from below (it's also included in the zip file above).

 ////////////////////////////////////////////////////////////////////////////  
 //  
 // This file is part of RTIMULib-Arduino  
 //  
 // Copyright (c) 2014-2015, richards-tech  
 //  
 // Permission is hereby granted, free of charge, to any person obtaining a copy of   
 // this software and associated documentation files (the "Software"), to deal in   
 // the Software without restriction, including without limitation the rights to use,   
 // copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the   
 // Software, and to permit persons to whom the Software is furnished to do so,   
 // subject to the following conditions:  
 //  
 // The above copyright notice and this permission notice shall be included in all   
 // copies or substantial portions of the Software.  
 //  
 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,   
 // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A   
 // PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT   
 // HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION   
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE   
 // SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  
 //  
 // Further modified by King Tide Sailing (kingtidesailing.blogspot.com) 
 //  
 ////////////////////////////////////////////////////////////////////////////  
 #include "Wire.h"  
 #include "I2Cdev.h"  
 #include "RTIMUSettings.h"  
 #include "RTIMU.h"  
 #include "RTFusionRTQF.h"   
 #include "CalLib.h"  
 #include "EEPROM.h"  
 RTIMU *imu;                      // the IMU object  
 RTFusionRTQF fusion;                 // the fusion object  
 RTIMUSettings settings;                // the settings object  
 // DISPLAY_INTERVAL sets the rate at which results are displayed  
 #define DISPLAY_INTERVAL 1000             // interval between pose displays  
 // SERIAL_PORT_SPEED defines the speed to use for the debug serial port  
 #define SERIAL_PORT_SPEED 4800  
 unsigned long lastDisplay;  
 unsigned long lastRate;  
 int sampleCount;  
 void setup()  
 {  
   int errcode;  
   Serial.begin(SERIAL_PORT_SPEED);  
   Wire.begin();  
   imu = RTIMU::createIMU(&settings);            // create the imu object  
   Serial.print("ArduinoIMU starting using device "); Serial.println(imu->IMUName());  
   if ((errcode = imu->IMUInit()) < 0) {  
     Serial.print("Failed to init IMU: "); Serial.println(errcode);  
   }  
   if (imu->getCalibrationValid())  
     Serial.println("Using compass calibration");  
   else  
     Serial.println("No valid compass calibration data");  
   lastDisplay = lastRate = millis();  
   sampleCount = 0;  
   // Slerp power controls the fusion and can be between 0 and 1  
   // 0 means that only gyros are used, 1 means that only accels/compass are used  
   // In-between gives the fusion mix.  
   fusion.setSlerpPower(0.02);  
   // use of sensors in the fusion algorithm can be controlled here  
   // change any of these to false to disable that sensor  
   fusion.setGyroEnable(true);  
   fusion.setAccelEnable(true);  
   fusion.setCompassEnable(true);  
   Serial.println("Units");  
   Serial.println("Pose: Roll, Pitch, Magnetic Heading (measured in degrees)");  
   Serial.println("Gyro: Rate of Roll, Pitch, and Yaw (measured in degrees per second)");  
   Serial.println("Accl: Acceleration in X, Y, and Z (measured in g's, with 1 g equal to 9.8 meters per second squared)");  
   Serial.println("Time: Timestamp (measured in milliseconds from when the MPU-9150 was initiated)");  
   Serial.println("----------------------------");  
 }  
 void loop()  
 {   
   unsigned long now = millis();  
   unsigned long delta;  
   int loopCount = 1;  
   while (imu->IMURead()) {                // get the latest data if ready yet  
     // this flushes remaining data in case we are falling behind  
     if (++loopCount >= 10)  
       continue;  
     fusion.newIMUData(imu->getGyro(), imu->getAccel(), imu->getCompass(), imu->getTimestamp());  
     sampleCount++;  
     if ((delta = now - lastRate) >= 1000) {  
       sampleCount = 0;  
       lastRate = now;  
     }  
     if ((now - lastDisplay) >= DISPLAY_INTERVAL) {  
       lastDisplay = now;  
       RTVector3 pose = fusion.getFusionPose();  
       RTVector3 gyro = imu->getGyro();  
       RTVector3 accel = imu->getAccel();  
       float r = M_PI/180.0f;      // degrees to radians  
       float d = 180.0f/M_PI;      // radians to degrees  
       float roll = pose.y()*d*-1;    // left roll is negative  
       float pitch = pose.x()*d;     // nose down is negative  
       float yaw = pose.z()*d;      // 0 Yaw = 270 magnetic, this gives left or right up to 180 degrees  
       float hdm = yaw-90;        // converts yaw to heading magnetic  
       if (yaw < 90 && yaw >= -179.99) {  
        hdm = yaw+270;  
       }  
       float ror = gyro.y()*-1*d;    // rate of roll  
       float rop = gyro.x()*d;      // rate of pitch  
       float rot = gyro.z()*d;      // rate of turn  
       float ax = accel.x();       // acceleration on the x-axis  
       float ay = accel.y();       // acceleration on the y-axis  
       float az = accel.z();       // acceleration on the z-axis  
       int ts = imu->getTimestamp();   // the timestamp  
       Serial.print("Pose: ");  
       Serial.print(roll);  
       Serial.print(" | ");  
       Serial.print(pitch);  
       Serial.print(" | ");  
       Serial.println(hdm);  
       Serial.print("Gyro: ");  
       Serial.print(ror);  
       Serial.print(" | ");  
       Serial.print(rop);  
       Serial.print(" | ");  
       Serial.println(rot);  
       Serial.print("Accl: ");  
       Serial.print(ax);  
       Serial.print(" | ");  
       Serial.print(ay);  
       Serial.print(" | ");  
       Serial.println(az);  
       Serial.print("Time: ");  
       Serial.println(ts);  
       Serial.println("----------------------------");  
     }  
   }  
 }  

You should now see something like this screenshot here. If not, make sure you are using 4800 baud (the standard for NMEA 0183, which is what we'll be using in the near future). Also, this has a refresh rate of 1000 milliseconds, which is fine for this--but you can change that to whatever you want by editing the DISPLAY_INTERVAL in the top of the code.

The raw output is kind of screwy, but fortunately it's quite easy to make it less screwy. For instance, roll is measured about the y-axis (hence the declaration of roll coming from the y axis data). Negative roll is to the left, negative pitch is nose down, and negative yaw is degrees to the left of 270 magnetic heading. There's a quick function to convert yaw into magnetic degrees, so you actually don't see the raw yaw value.

The raw output is also measured in radians or radians per second. That's useful if you're going to be using trigonometric functions later on (which I will be, in which case just convert it back to radians), but for reading this data, the degree is the ideal unit. So I included two variables, r and d, for converting to radians and to degrees respectively.

Also note that since this sensor was resting flat on the table, there is ~1 g in the Z direction for acceleration. If I didn't see that, I wouldn't care because gravity will have ceased to exist. So all this means is that the positive Z direction is pointed downward through the sensor--when in doubt, just check the schematic printed on the sensor itself. If you need to flip the directions, just multiply the variable by -1, as I did for a few.

9 Degrees of Freedom!

Now it's all set! You can edit the code as necessary to fit your own application, using the simplified version that I have supplied here to manipulate the variables. Next up, I'll show you how to make a maritime NMEA-0183 device that calculates magnetic heading, true heading, roll, pitch, yaw, and all their rates.

How To Install OpenCPN on a Raspberry Pi


EDIT 11 FEB 2016: I would now recommend simply installing the OpenPlotter software found here (it's basically a fresh install for the Raspberry Pi with OpenCPN already installed and running): http://www.sailoog.com/en/blog-categories/openplotter-rpi

EDIT 24 MAY 2016: I've also gone and updating this post to reflect the guide at OpenCPN, instead of just winging it on my own.

Recommended Gear

Easy Step-By-Step OpenCPN Install/Compile on a Raspberry Pi for Total Noobs

If you follow the OpenCPN documentation, there are two options. The first option says you can install it without compiling it from Launchpad, but I'll be totally honest--I tried going that route, and I couldn't get it to work because, as I've mentioned in multiple places, I'm not exactly a very computer literate person. I'm very good at copying and pasting into a Terminal window, and that's what we're going to do here. Their second option is compiling OpenCPN, which as far as I can tell, means building it from scratch, which is much easier than it sounds. Here's how you do that:

Get a fully working Raspberry Pi (I'm using a Raspberry Pi 2 Model B+), with the latest edition of Raspbian installed, and run the following commands in the Terminal:

sudo apt-get update
sudo apt-get upgrade
     # When it prompts you, type "Y" for Yes and hit enter. This may take a while
sudo apt-get install build-essential cmake gettext git-core gpsd gpsd-clients libgps-dev wx-common libwxgtk3.0-dev libglu1-mesa-dev libgtk2.0-dev wx3.0-headers libbz2-dev libtinyxml-dev libportaudio2 portaudio19-dev libcurl4-openssl-dev libexpat1-dev libcairo2-dev
     # When it prompts you, type "Y" for Yes and hit enter. This *will* take a long time, about 5 minutes depending on your internet speed
git clone http://github.com/seandepagnier/OpenCPN -b rpi_fixes
cd OpenCPN
sudo mkdir build
cd build
sudo cmake ..
sudo make -j3
     # This takes a long time. It will probably crash, too. See expanded notes below.
sudo make install
sudo nano /boot/config.txt
     # Add these two lines so it can run on slow hardware:
     framebuffer_depth=32
     framebuffer_ignore_alpha=1
cp /usr/local/share/applications/opencpn.desktop /home/pi/Desktop/
     # this creates a desktop shortcut
sudo reboot

The first two lines,

sudo apt-get update
sudo apt-get upgrade

are there to make sure all the items from the next line point to the correct web addresses to download. At least that's how I understand it. It's always good to run those two commands whenever you're about to download and install things using apt-get. It will prompt you if you want to download the new files, which will take up XXX megabytes, and simply type "Y" and then enter to confirm that you do. This might take a few minutes.

The next line,

sudo apt-get install build-essential cmake etc...

downloads all those libraries and background programs that OpenCPN requires. This one takes a while, and again, you'll have to confirm that you want to download them all by typing "Y" for yes when it prompts you.

The next line,

git clone http://github.com/seandepagnier/OpenCPN -b rpi_fixes

downloads the official and latest OpenCPN program from GitHub. GitHub, if you're not in the loop, is basically where many programmers keep their code for ease of use and easy access. If you know what you're looking at, it might be helpful to go check it out sometime. Now, why use Sean Depagnier's OpenCPN instead of the official one? Because it's optimized for the RPi, and won't crash as much as you install it.

I use GitHub because it makes it easy to download scripts and files straight from the terminal (in fact, you don't even need a monitor, keyboard, or mouse with the Raspberry Pi for this reason--you can SSH in and do it through the terminal on your laptop or desktop). Once you download all those files from GitHub, we still have to integrate it with all those other libraries we downloaded, and actually install it.

These few lines,

cd OpenCPN
sudo mkdir build
cd build





make a build folder inside the OpenCPN directory, which is what we use to build/compile/install OpenCPN. The next commands,

sudo cmake ..
sudo make -j3
sudo make install

do the actual installation of OpenCPN. It takes a while to accomplish, especially because the RPi isn't exactly a high powered processor. The -j3 basically helps to prevent crashes. But it still probably will crash after you do the sudo make -j3 command... in which case, unplug the RPi, wait a few seconds, plug it back in, and then:

cd OpenCPN/build
sudo make -j3

This will restart the process right where you left off. Repeat as necessary. It crashed on me three times. Don't confuse a crash with the monitor from turning off (I deal with this later).

When it's installed, do this:

sudo nano /boot/config.txt

Add these two lines to the file:

framebuffer_depth=32
framebuffer_ignore_alpha=1

Now that OpenCPN is installed, let's make it nice and polished for use. First of all, we'll need some charts.

Downloading Raster/Vector Charts for OpenCPN


This is a guide for easily getting US charts for OpenCPN. Allegedly, the vector charts don't work well on a Raspberry Pi, but I didn't notice anything slow on my 2 Model B+. This is totally up to you which one you want.
Follow those links and download whichever state you desire. When it's done downloading, unzip the file to wherever you want, and then open OpenCPN. I found that the only way to unzip them is to right click, then "Extract To Here" and then copy/paste the folder after that.

Setup a USB GPS Puck with OpenCPN


Simply plug it in to one of the USB ports on the Pi, launch OpenCPN and go to the Connections tab under Settings. Click "Add Connection", select "Serial" ("Network" would be for Wi-Fi or Ethernet cable connections), click the DataPort drop down, and "/dev/ttyUSB0" should show up. If you're not sure which USB port it is, simply unplug the GPS, in a terminal type "ls /dev/tty*" and make note of how many USB devices are there, then plug in the GPS, and retype in the same command, and whichever one is added is the GPS. Select it, leave everything else the way it is, and click Okay. It will automatically start locating you, and if you click on the red boat icon on the toolbar (the "Auto Follow" option), it will center on you.

Setup I2C Devices with OpenCPN


If you have a low-cost $10 IMU Compass, it's pretty easy to setup to use with OpenCPN, although not as easy as a GPS. You'll need a script to process this, and fortunately I've gone into great detail on how to do this here. If you're not using kplex, simply add a Network connection in OpenCPN, using the UDP port as described in that script.

Connect NMEA-0183 to OpenCPN


I've also gone into great deal about this in another post. Basically, you'll follow the same instructions as the GPS, selecting USB0 or USB1 or whatever as your input in the OpenCPN options. Or you could also check out the multiplexer concept too.

Prevent the Monitor from Automatically Shutting off on a Raspberry Pi


You'll want to disable the screen's auto-turn-off feature, since you probably don't want the screen to turn off in the middle of navigating a channel. To do this, type in: 

sudo nano /etc/lightdm/lightdm.conf

"nano" is a text editing program built into the Terminal program. For the uninitiated, it might look kind of daunting; but it's quite simple. That command will open up the lightdm.conf file, allowing you to edit the necessary parameter to keep the screen on. Using the down arrow key, press down until you find [SeatDefaults] and add the this line of text just below it:

xserver-command=X -s 0 dpms

Then press CTRL+X, then Y, then enter to save and exit the nano editor. VoilĂ ! Now after you reboot the Pi, you're screen will never turn off unless you pull the plug.

Sailboat Compression Post Repair and Replacement

Pre-Surgery Planning

Before you start tackling a project like this for yourself, please read this all the way through. It isn't because I want you to do what I did, or because I somehow want to show off my techniques, or anything like that--it's because I did it wrong, and I don't want you to make the same mistakes.

I had three clues as to what kind of repair I would have to do. The rust color in the crack at the foot told me that somewhere in there was rusting steel and water intrusion somewhere underneath the mast. The port side of the deck was visibly higher than the starboard side, which could have been because of improper block replacement (all the halyard blocks were attached to the deck, not the mast) or something more nefarious. Finally, the bolt on the head door to lock it closed against the compression post didn't work. It was out of alignment which meant, to the greatest despair a boat owner can know, the compression post was bent. And you could see it, too.


No one knew what was wrong. I consulted multiple friends in the industry, and all of them said the same two things: it needed work, and they were too busy. So I decided to tackle the project myself (in my hard-headed attitude that typically gets me in trouble and drains my wallet). I had the mast craned off at a boatyard, I had them replace the standing rigging while I motored back to my marina to get my hands dirty.


Very, very dirty.









Recommended Tools and Gear

Removing the Tumor

After the mast was removed, the head door bolt fit back into place. Clearly the post was under too much pressure with the weight of the mast on top. Using a chisel, I scraped away the ancient sealant used to cover the mast foot and discovered four inconspicuous screws. Two of them sheared in half, and the third one had completely punched out of the shoe, so it wasn't even attached at all. And the fourth screw was wedged in so tight (not surprising, given that the bulk of the entire load was directed through that one screw), I had to use the dremel to drill new lines for the screwdriver.

With the mast shoe removed, the cracked gelcoat underneath finally saw the light of day for the first time in probably 40 years. Using the dremel and the wood chisel, I chipped away the damaged gelcoat. Even at this point, I knew all of it would have to be replaced, so it didn't hurt too much to rip the skin off my boat. And a frightening discovery awaited me underneath it all.

The steel compression plate had corroded so much that it flaked away with just a light scraping. It wasn't secured in any way (maybe it was at some point), so it just kind of fell off out once the gelcoat was gone. It was like
finding a tumor in your child, and probably almost just as satisfying to remove it. But that was only part of it.



The deck was warped, and the gelcoat was still cracked. Even though I removed part of the problem, the deck would've rotted. A few pilot holes confirmed my fears--the core was wet. So, in the interest of cosmetics and new deck blocks, I blocked out a square chunk of deck to replace.

I used a circular saw (carefully measured so as to only cut through the upper fiberglass and the core, and not the lower layer of fiberglass (that served as my cabin ceiling, after all)) to make the big cuts, and then a hammer and chisel to remove the rest. And a grinder. And a dremel. And a router for the edges, to make it a little finer than the circular saw would allow.

So now, at this point, I had removed the mast shoe (still good, though it needed a little touching up), the rusted compression plate (not good), and cut out a nice big square of deck, down to the lower layer of fiberglass (definitely not good). It was a very disheartening sight.

Engineering a Solution

The compression post had to go. It was bent, and would only exacerbate the problem if used any more. So I pulled it straight out from the top (removing four screws that secured it to the bulkhead first), and when I laid it out against a straight plank on my deck, it was clearly bent. Well, I certainly didn't want to use wood again.

Using the advice of one of my boat neighbors who saw me working, I visited the Millerick Bros. Marine Construction a little hike up north. I worked with them, and we engineered a suitable replacement post made of marine grade aluminum--much better than a pliable wood thing, if you ask me. The bottom of the post had a small plate welded on to distribute the pressure over a wooden foot on the keel; instead of the post pressing directly on the ceiling (and the pressure plate above), the pressure plate would be welded directly on top of the post, and would distribute the pressure from below. This whole post would then through-bolted on the deck, through an accessory plate (I used this one from Garhauer Marine), through a piece of PVC plastic, and then finally through the mast shoe itself. The whole thing would be one, rigid, immovable unit. (It should be mentioned that the plastic was there to separate dissimilar metals, since the Garhauer plate was stainless steel, and the mast shoe was aluminum--dissimilar metals should never touch in a saltwater environment due to electrolysis).

They gave me all the pieces to the new post before the welded them, and insisted I do a "mock up" to make sure it's all good. And it was very good that they recommended that, because I made a curious discovery.

The center of the post was offset from the center of the mast to starboard by 5/16 of an inch. I don't know what sort of naval architecture principle that adheres to, but it seemed to explain why the old post bulged out to the right. Well, I figured the new post should be the same as the old one, since maybe there was a specific reason for the offset (maybe in the keel?), but more importantly, if I were to make the center of the post 5/16" to port, then I would've had to remove part of the bulkhead, cabin sole, and the holding tank. I was already hurting on time (remember, my rig was getting worked on and I was paying for storage every day at the boatyard, but on top of that, I had a looming deployment coming up). So I quite wrongly chose to keep it the same as before.





























The New Compression Post

They welded that thing together, and goddamn, I was a happy man picking that up. The first step was to epoxy in a piece of marine grade ply to the keel, which is where the post would rest. I sanded and painted a piece, put it in, slid the new post on, saw it wasn't even, removed it all, sanded again, put it all in again, saw it was still tilted, and repeated this whole process over and over again until the piece of wood was shaped such that the post rested completely straight up and down on top of it.

But how the heck do you know when it's straight up and down? It's especially difficult on a moving, roving boat, that's for sure. But I came up with a genius way to approximate the post's position to a degree good enough for my non-industrial purposes. For roll (left and right), I couldn't use the bulkhead because that warped and wasn't straight anymore. So instead, I cut a dowel so that it just fit snugly in between the post and the non-supporting bulkhead on the other side of the companionway. 

Then I ran the dowel up and down the post, and if it was straight up and down, it would fit; if it wasn't, then it would either jam or there would be a gap. And thus, I finagled the post until the distance between the dowel and the post remained the same as I ran it up and down between the post and the opposite bulkhead.

As a way to crosscheck this, I ran a bungee from one handrail to the other on top, and made it so that the distance between the bungee and the mast shoe was the same throughout.

How about pitch (forward and back)? I used the same principle, but instead I used a compass (the kind with two points for drawing circles) and used the door to the head. The door was secured to the bulkhead for the captain's quarters, well away from the warped compression post, so I felt safe relying on that too.

As for rotation (twisting the post left or right), that was pretty easy on deck, using the compression plate and the lines on the non-skid and handrails. And for physical forward and back placement, I tied a line between the chainplates and made sure that line passed through the center of the compression post.

Once that bottom marine ply was good, I epoxied it in place. Then I placed the post on top, verified it was straight, and all was well.

Stitching the Wound

I used West System Epoxy for this project. More specifically, I used the West System 105 Resin, the West System 206 Hardener (for my environment; see this guide to determine which hardener to use), colloidal silica as my additive, and I also bought the calibrated pumps (be sure to get the appropriate size pump for your container).

To prepare the surface, I sanded it, vacuumed it, wiped it down with acetone (if you use acetone, be sure not to let it touch gelcoat), and made sure it was nice, clean, and dry for application. Then I mixed the resin, hardener, and silica (adding silica until it had a mayonnaise-like consistency), mixed it for sixty seconds, then got to work.

First, I coated the surface with the epoxy mixture, then applied a layer of fiberglass, and then applied another coating of epoxy, basically making a fiberglass sandwich. I tried a few different types of fiberglass, but I am convinced that the only kind to use is woven roving, but that might be because I was working in a unsuitable environment (lots of wind... so much wind).

Once I had a bottom layer of glass, I sanded it down, cleaned it, made it nice and smooth, then fitted in a piece of 1/2" marine plywood for my new core. This part took a while, and because of the warped deck situation (the port side was higher than starboard), I ended up using two pieces of wood. Once again, lots of sanding, cleaning, and more sanding, until everything fit snugly. In order for the top layer of new glass to bond to the old layer, I kind of ground out a "V" at the edges, so that the two layers would overlap partially. My marine maintenance bible recommends a 1 to 12 ratio for this, such that if the top layer of fiberglass was 1/8" thick, then the bevel would go out 12/8" from the edge of the new core.

One more sanding, one more cleaning session, and then I liberally applied epoxy into the wound. Then I wedged the new core in, smearing the epoxy around the cracks, making sure there were no gaps anywhere. Then I put a layer of paper on top, and a few bags of rocks on top of that (sand would work better, but was unavailable for me). This is to keep positive pressure on it while it cures, ensuring it has a tighter bond to the rest of the hull.

Finally, no more hole in my boat!

After it cured, I removed the bags, the paper, and sanded it lightly. More cleaning, a layer of epoxy, a layer of fiberglass, a layer of epoxy, sanding, cleaning, epoxy... it was tedious, but the end product was rock solid.

Interestingly, I realized a crucial point that I failed to notice before. The previous design had the plate on top of the deck, which raised the mast a little bit. My new design had the plate below deck, which did not raise the mast. So to compensate for this, I made a "bump" as illustrated in MS Paint, such that the mast shoe had a few extra layers of fiberglass underneath it so it would be raised slightly as it was before. This was crucial, because my rig, although new, would be the same dimensions as before.

Okay, things were looking great at this point. I had a new core, with a few layers of fiberglass on top, a water tight deck once more, and a straight compression post! The last part was to really make it as even as possible.

But for this step, the mast shoe, not the deck, was most important. So I went below, drilled the holes up through the plate and deck, attached the halyard organizer, plastic separator, and through-bolted the mast shoe. I used a string between the hand rails (in line with the chainplates) to gauge which part was uneven and where to grind. I also used a plank and the hatch lid as a straight edge to assist in this step, and once everything was sanded, cleaned, straight, and level, I...

That’s actually a lie. It wasn't satisfyingly flat, and it never will be. None of the steps of this project were completed to 100% satisfaction. If I had the boat in a factory, out of the water, on the hard, with state-of-the-art equipment and every tool you could imagine, then I could do it to satisfaction. But I wasn’t in a factory, in a warehouse, or even on land. I was next to the ocean with only a few days to complete my project. So I had to settle with a technique that I used when flying: the TLAR method, or the “That Looks About Right,” method. So, I made it look about right, then moved on to painting.






Cosmetic Surgery (Not Really)

Because of my time constraints, I didn't put much into the cosmetics of this project. I put on a few coats of EasyPoxy Primer, made sure it looked "nice enough," then motored over to the boat yard for the mast to go back on. It was absolutely crazy, because I wasn't even ready yet. While they got the mast ready, I worked feverishly to finish my project.

Before I drilled anything down, I used a drill bit to bevel the holes in the deck very slightly. I put a bunch of Dolphinite in the new space, so as to give a last line of defense in case water managed to get down there. Then I slathered the bottom of the halyard organizer with it, stuck it on, put the plastic separator in, and placed the shoe on top. Then I covered my new stainless steel bolts with it (so the contact between stainless steel and aluminum would be minimized), slid them through, slathered the washers and nuts, and tightened it down. Marvelous.
Next up, I put the wire pipe in (the little pipe through which all the wires go), coated the bottom of the shoe with Dolphinite, and it was ready for the mast.

They put it back on, and one of the guys recommended I put some sort of protection around the post. He said it’ll support the mast no problem, but if you’re sailing around and get knocked down, and someone falls into it, giving it a strong side load right in the middle, it might not be good. That wasn’t an issue, because I was going to wrap the old post around it. It had to go in anyway, since the head door needs it and it’s aesthetically more pleasing. So I cut up the old post so that it would fit, and made a cool little cover for the aluminum using the original wood. The headliner was super simple too--a local fabric store had the correct color of headliner (it's typically the same for cars, too), and I bought a sheet of foam for insulation to go behind it. Then I used the old headliner pieces to kind of make a template, and after I stapled the headliner back up around the edges (with the foam insulation behind it), I took a step back and surveyed the scene before me.

The project was completed.

Final Thoughts

My initial quote from my friend’s “boat guy” was $3-5000, but that was just for taking the rig off and replacing the deck core. And that was also just labor. If you added in engineering a new post, installing it, all the work that I did except charging myself an hourly rate—it would be closer to a $10,000 project. The new rig itself was a couple thousand dollars, so if I had elected to give it to someone else, I would be looking at shelling out at least $15,000. And since I bought the boat for $20,000, that would have hurt a lot.

What did it cost me, then? The Millerick Brothers were the most expensive part at $320 for everything—consultation, supplies, labor. The Dolphinite was $50, the epoxy and fiberglass came to about $150, the marine plywood was $80. Add in all the miscellaneous parts and tools, and it probably came down to about $800 for me to do this project. Basically less than a tenth of what it would have cost me otherwise.

A few lessons learned: don’t skimp on tools. You might think you can save money by buying a tool and using it for everything (read: Dremel), but you’re just wasting your time and money. Get a nice router. Get a nice grinder. Get a good toolkit with ratchets and screwdrivers and a power drill with plenty drill bits. You’ll have to pay the price at once point, whether earlier or later. And the finished product is much higher quality with good tools than with shoddy ones. And you should strive for the best job that you can do, and that’s only possible if you have good tools.

Don’t rush yourself. I was forced to rush the job towards the end because of my military obligations, and it could have been a much high quality product if I had plenty of time. On the other hand, realize that unless you are in a factory working for a company with a budget of thousands of dollars, you can only do so much. You can strive for perfection, but you can’t expect it if you’re doing a DIY project like this.

And then finally talking vs doing. I talked about this project for a year. I even had the boom off for 4 months while trying to figure out what to do. But I just couldn’t do it. I don’t know why—I just had such a hard time actually committing to the work. I said I would just hire someone to do it, but never did. I said I had to research more about the boat and the issue at hand, but that is a never-ending road. I kept putting it off, putting it off, coming up with what I thought were good reasons to delay.

During this time, I read Sailing to the Reefs by Bernard Moitessier, and he describes preparing his first boat to go cruising with two other friends. One of them was all talk and all money, but he never actually got off his ass to do anything. He had the desire, but not the tenacity to do what he said he wanted to do. So Bernard left him behind.

After reading that passage, I felt like his friend. Like I was just talking about doing it, maybe inching closer, maybe throwing some money at it. But not actually doing it. After I read that, I called the boat yard and scheduled an appointment.

I wish, however, I had figured out a way to make the post completely centered. The 5/16 of an inch offset will keep bothering me as long as I own the boat. Eventually it'll warp like before. But I like to think that if it took forty years for a piece of teak to warp, it'll take at least that long for a hunk of aluminum.

Another thing--I wish I had rewired the mast. Now that I'm looking at installing an anemometer, I'm going to have to lift the pole again, and it would've just been simple if I had done it all at once.

But you know what? The fact that I tackled this serious project myself is what makes me so damn satisfied. Through careful planning and consideration, I designed this newer, cleaner, stronger version of my boat. It wasn't all just me--I couldn’t have done it without Jerry from two boats down lending me his tools, or without the services of the Millerick Brothers.

And you know what? Every time I look at that post, at the deck, at the mast even, I think to myself, "That looks about right."