NMEA 0183 Speedometer

I had an email request to find this project from 2010… I updated the code too!

Arduino in Test Mode

Arduino in Test Mode

This project  takes the pulse from a paddlewheel boat speed sensor and makes an NMEA output on the serial port.  It should work with most through-hull speed transducers, and also with windspeed transducers too.  The NMEA stream comes from the arduino serial port, so if you have a USB arduino you can hook it up to a laptop easily.  If you want to hook it up to another NMEA instrument you’ll need something like a MAX232 chip to take the TTL logic from the serial port pins and convert them to RS-232 (or convert to RS-422 to meet the NMEA specification).

I used Valis’s NMEA monitor program (NavMonPC) to check out my output string.

The top picture is of the basic sensor in self-test mode.   The arduino makes a pulse for testing that should show 5.5 kts.  The yellow wire connects the pulse output to the pulse input to be measured.

Here is a screenshot of the output of the NMEA string received by hyperterminal.

Here is a screenshot of NavMonPC showing the display.

On power-up the microcontroller waits for a +++ sequence for a few seconds, if it receives it, it enters a calibration mode so you can change things like the filter pole and the number of pulses per mile.  You can also change these in the code itself.

To test the unit, I have it creating a pulse using the Tone() function.  If you wire the output pin to the input pin you should get around 6.3 knots.  The input would normally be connected to a signal that is either a TTL pulse signal (0-5 Volts square wave) or a switch connected to ground alternating between an open-circuit and shorted to ground.  Here’s a picture of the wiring:

Here’s the code  (updated Sept 2017 for modern arduino, tested on an Uno)

I tested it with a speed transducer my dad gave to me from his box of parts.  It’s from a raymarine st-50 system, the tag says Airmar Z092.

picture of transducer

Airmar Transducer

I put 13V to the red wire, ground from the arduino and my power supply to the bare wire, and connected the green wire to pin 3 THROUGH a 10K OHM RESISTOR.  The resistor is important– the atmel microcontroller has diode clamps to protect against over/under voltages but you need to limit the current so the diodes don’t blow up.  If your input is in the 0-5V range you don’t need the resistor.


27 thoughts on “NMEA 0183 Speedometer

  1. Ebbe Hansen says:

    Simply amazing! Great instruction
    I pasted the code into a 5 dollar Arduino uno. Connected the output to my wifi multiplexer and the speed readings 6,3 knots show up in any ios/android app that supports the nmea sentence. Incredible
    My paddlewheel I believe is creating 1pulse per rotation. So I guess a bit of calibration is all it takes to transform my analog paddlewheel sensor to an nmea 183.
    I´ve been trying to figure out what code string to do some trial/error (See if the 6.3kts goes up/down when using the test signal). But I´m no code reader!! If you could point out for me would be great.
    First of all thanks for sharing this nice piece of work. We are tons of sailors out there with a blend of old and newer instruments that we would love to connect and combine the data, without getting ruined. Thanks
    Br Ebbe

    • Mike Holden says:

      Thanks for your comment! There is a calibration routine, if you type +++ at the terminal window in the first 5 seconds after boot, it will walk you through changing the parameters. It’s a little awkward with the arduino terminal with its send button. If you don’t use this you can directly change the code.

      There are 2 constants in the code for the speed per pulse. If you double either you should get 12.6 kt with the test pulse. The first is how much distance the boat moves per pulse and the second is a fudge factor used in calculating speed.

      If you want to change the constants in the code look for the lines:

      #define DEFAULT_KMI_PER_PULSE (1./20000.) // nautical miles per pulse (Airmar says 1/20e3)
      #define DEFAULT_SPEED_SCALE 1.00 // scale factor for calibration. (multiplies kmi_per_pulse)

  2. peter white says:

    Like your take on repurposing a depth sounder transducer. I bought a yacht in which the head unit was broke to this will give be an opportunity to get a display in the cockpit. Nice.

    Have you considered running the code on an ESP-32? Its faster and has embedded bluetooth and wifi.

    • Mike Holden says:

      This code should work on an ESP board. The EEPROM read/write and pin change interrupt code would be slightly different however. And if you wanted to use bluetooth or wifi you would need to add that code. I have used ESP8266 boards for several projects and think it is a fine idea if you wanted to extend the basic functions in this project.

  3. kevin says:

    Thanks so much for your great article. This is my first arduino project and I was able to get the sketch compiled and uploaded but I’ve got too issues.

    1) I keep getting a bad data from EEPROM message on init

    2) I’m able to see the data on the serial montior and hyperterminal but unable to get the data on NavMon

    Any advice is greatly appreciated. I’m currently using the jumper from 10 to 3 until the paddlewheel arrives.


    • kevin says:

      #2 Issue resolve by going to File -> connections -> serial port and setting to correct com port and hitting the connect at the bottom of the screen. Somehow I missed the connect option earlier. Thanks!

      • Mike Holden says:

        Glad you figured out the NavMon settings. The eeprom message will be there until you run through the calibration steps, it uses default settings when there are none saved in eeprom. To go into the calibration routine, type “+++” within the first 5 seconds of startup. Then follow the prompts. Using a real terminal program rather than the arduino serial monitor will make this easier but it can be done either way. Some options are here: https://learn.sparkfun.com/tutorials/terminal-basics/all

        • kevin says:

          Thanks for quick response. I’ve gotten through the calibration and no more bad eeprom. I’m trying to test with a Airmar ST650 speed sensor that has three wires, Blue, Black and Shield. I’m powering the uno r3 through usb and have tried connecting the wires in all the ways I can think but still getting no response. I should mention I’m a complete novice in arduino and only understand the basics of electronics. Any thoughts?

    • Mike Holden says:

      A reseller (https://maxmarineelectronics.com/product/datamarine-astx-20bth-31-279-1-51-speed-insert-only-3-wire-st650/) says maybe blue=12V (usually red); shield=ground (both power supply ground and arduino ground); black=signal to arduino. Don’t forget the 10k resistor on the arduino pin when running more than 5V into it. If you accidentally put 12V into the arduino anywhere you may have burned it out (sometimes just that pin dies, sometimes the whole chip) so be careful. I don’t know if this is correct.

      • kevin says:

        Thanks so much for your comment. I’ve verified that pin 3 is still operational with the jumper wire to pin 10. Great feature you added there! I’ve also verified that the paddlewheel oscillates voltage on 90 degree turn 5 another 90 degree 0 but still get 0 for speed in hyperterminal and serial monitor. Not sure where to go from here. Thanks again for all of your help, code and inspiration!

  4. kevin says:

    I’ve discovered using a multimeter that the output of the sensor, the link you showed is the exact one, alternates between 0.3 and 0 which is not enough to trigger a signal on the arduino. I’m thinking it is either a bad sensor or you would have to amplify somehow to trigger a high signal. Thanks again for your code and thoughts!

  5. Michael says:

    Thank you very much for sharing this piece of code.
    I need the STW in nmea on my boat for a openplotter/opencpn tactical processor. But I have a Seatalk Bidata: spiting out only seatalk data. So first I thought of taking an arduino and translate the seatalk into nmea – which isn’t that easy. Than I found your site and this is the solution.
    Everything is ready, I’ve put the code on the arduino and it runs with the demo data. Great think – thank you….

    One think I’m still bothering about: my Hull sensor is with three wires, so I assume its a hall-effect sensor type. So I have positive and grounds going in, and I have the signal coming out.
    Do you know or think, that it is possible to have the signal wire used for both systems: seatalk and nmea? So splitt the signal in a Y type of cable and use it for the arduino to calculate nmea and the other end for the seatalk device in the cockpit which gives me the speed and depth.
    Grounds should be common grounds as I do not think that the Bidata-device is opto isolated. So just feeding the pulse in the arduino should be fine. But can the same pulse also feed the seatalk device at the same time?

  6. Andrew Clark says:

    Hi Mike, great project and thanks much! Got it running on my Arduino straight away but not certain how to connect to my speed log as it’s a 2-wire BNC. I am assuming it is a make-break kind of signal, alternating between open and closed circuit. Question is how to connect…do you recommend a simply pullup high so that when “closed” a 5V pulse is send to pin 3? Or is there a better method? Thanks again!

  7. don says:

    I am probably late to the party, but I can not find the actual code for the sketch. If this is still active, I would love to pursue this. I want to connect a paddlewheel to a nmea 183 (two wire) input on a Garmin gps map so that this will accept the data and then send it to my Axiom via nema 2000. The Garmin to Axiom connection already works for depth and other data, I just want to get the paddlewheel working that is change pulses to nema 183 sentences that the Garmin will take care of converting to nema 2000. The holes I have in understanding are A) the sketch and B) the details on chaning the arduino searl output into the nema 183 compatible formats. ANY help at all would be very much appreciated thanks.

  8. Alex says:

    Hello, I’m a newby on Arduino and I ask Your advice to solve an issue that probably meet the interest of a lot of sailors that as me have on board old but fashion analog displays.
    I’ve installed a new electronic from whom get NMEA0183 sentences (speed, wind, depth, compass) and I would like to translate these in analog SIN COS signal.
    Any advice?
    Many thanks in advance

    • Mike Holden says:

      The arduino can make analog signals in the 0-5V range with the PWM hardware. I’m not sure of the voltage range for a sin cos display but I would imagine you will need an amplifier or other signal conditioning to translate the voltages (using an op amp or similar circuit). I haven’t done this myself but it is probably feasible!

  9. John says:

    Hi, thank you for a great project!
    I just got it working with a similar transducer that you are using. Mine runs on Arduino Pro Mini.
    I wonder why the readings are not decreasing but they stay high after stop rotating the paddlewheel. If I slowly decelerate speed the readings go lower but still never to zero.

    • Mike Holden says:

      There is a low-pass filter in the code that may be the cause for this. This kind of filter does math very similar to a running average to smooth the measurement, and you can adjust the time constant (or filter pole) to adjust how aggressively the data is smoothed. If you want to eliminate the filter you can just change the code to use the most recent measurement rather than the filter.

      The filter code is this part:

    • adt = a_filt*(float)dt_update*1.0e-6;
      if (adt < 1.0) { speed_filt = speed_filt + adt*(speed_raw - speed_filt); // for stability a*dt < 1 } else { speed_filt = speed_raw; // un-filterable (shouldn't happen if set up properly and working) }

    • Just replace it with:

      speed_filt = speed_raw;

  10. John says:

    Thank you. I tried to change the code but for some reason readings never go down to zero after the paddlewheel has stopped spinning. Numbers decrease for a short while but then they stay at that level.
    I tried to translate the code but couldn’t quite understand. Should this reset the display to zero?

    // if it’s been too long, fake some 0 kt data
    if ((float)(micros() – last_update_time)*1e-6 > 2.0*dt_print)
    dtpulse = (long)(kmi_per_pulse/.001*3.60e9*speed_scale); // should give 0.001 kt
    gotdata = 1;

    • Don Winchell says:

      John and Holden and whomever. First, another thanks to Holden for getting me going on this. Now a comment. I did make some progress with it myself, but I stopped a few time. Then, when I tried my existing paddle wheel, it turned out to be no good, burned out, worn out, not working. Then I changed tack (boat speak), and took a shot at replacing the paddle wheel SIGNAL with a GPS signal so that at least my wind instrument would report some version of “true wind”. I was successful. Here is a link to all the details, including the code, the hardware used etc. Again, more of a hobby determination mission than anything actually practical. But yeah, it does work.

  11. Alexander S says:

    Hi Mike. Thank you for sharing the project. I have a question about the program code. Does “a_filt” equal to 1 (the default value) mean that one pulse occurs per full revolution of the wheel? If you set the value to 2, then when counting two pulses will be counted as one complete revolution of the wheel?

    • Mike Holden says:

      a_filt is the low-pass filter pole in radians/second, so is not for the pulse/rev like you ask. Low-pass filters do something similar to averaging the last few measurements, 1/a is related to how many of the last measurements are averaged together (time constant of the first-order system). Larger a does less filtering (averaging).

      If you are trying to scale the pulses per rev you want to edit the constant kmi_per_pulse, which is how many nautical miles per pulse, revolutions are factored out of this constant. The airmar sensor I had data for has a constant of 20000 pulses/nmi so kmi_per_pulse = 1/20e3 in the code as a default.

      • Alexander S. says:

        Thanks for the answer! Now I understand that this applies to the filter. I understand about the coefficient from the description in the code. I made (3d model) housing and impeller, now I’m trying to calculate the coefficient. Full-scale tests are needed.

Leave a Reply to kevin Cancel reply

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