Part 2: View rudder reference gauge on phone or tablet


Kool Project Summary:

This project is part 2 of how to build a direction of turn indicator (rudder angle indicator) using 5 LEDs, an Arduino Uno, an ethernet shield and a Raymarine Z230 or M81105 rudder reference transducer.  It builds on part 1 by adding the ability to show a gauge on your phone, tablet or computer in a web browser (works with iPhone and iPad as well).  This is great if you want to see your rudder position in a more remote location like the flybridge.

The circuit diagram and the sketch (program) to upload to the Arduino is provided below. The LEDs give you a quick visual summary of what position your rudder is in, and when it is dead center (white LED illuminates on its own). It should be fairly easy to adapt this project for a Garmin GRF10 rudder feedback sensor,  Simrad, Hummingbird, B&G, etc, or any resistive rudder angle sensor you want to use. The gauge is calibrated in reverse and shows as negative values because higher ohm values mean the needle is moving left which is usually the lower range on a gauge.

Difficulty Level:

Medium (basic familiarity with an Arduino and networking is needed, tons of great info online).


A rudder reference transducer is like a potentiometer in that when it turns it varies the resistance (ohms) through the circuit. The Arduino (computer) measures the resistance change as the transducer moves by using a voltage divider like an ohm-meter does. By identifying the values of your particular reference unit you can build non-overlapping ranges of ohms (resistance) that correspond to the direction you are turning. A hall effect sensor like Jack Edwards used to build his autopilot would work instead of the Raymarine transducer and would be less expensive. You would have to adapt the code to read voltage change. I picked a marine grade transducer because it is designed to be splashed with saltwater from the bilge area. Keep this in mind as you plan out your project.

HTML 5, Ajax, and javascript are used to code the gauge based on the work by Mikhail Stadnyk’s canvas gauge. Truthfully I’m not a web programmer so I hacked and tested. I’m sure everything can be improved!  For instance I’m not sure why there are funny markings on the gauge?

The work is pretty much done for you in the provided program. You will have to tweak the ohm values (in the arduino sketch) for variable R3 to match your specific transducer and rudder center point after everything is installed on your boat. To do this you can just open up the serial monitor window that is included in the free Arduino software you install on your computer, and watch the values as you manually move the rudder. You then tweak the code and re-upload it to the Arduino.  Test, test, test….

Project Justification:

Knowing the position of the rudder provides instant feedback when you are maneuvering your boat in a tricky situation. Knowing when your rudder is dead center makes steering easier and safer. Many autopilots no longer use a physical rudder sensor and have moved to virtual sensors. This has left more boaters without any kind of rudder angle indicator.

A dedicated display head that interfaces with a rudder reference unit costs hundreds of dollars. This project costs less than US $50 not including the transducer. If you can’t afford an autopilot or a dedicated gauge, you now have another option. An Arduino also provides an upgrade path for future projects like remote switching, temperature monitor, boat level gauge, autopilot, or whatever you can dream up.

As I explore the Arduino in future projects I hope to have the Arduino send Signal K formatted messages to an iKommunicate gateway to convert the data into NMEA 2000 format for display on a chart-plotter. An Arduino gives you lots of flexibility and options that a proprietary piece of hardware won’t. Of course it is just plain fun to tinker with this stuff!

Parts List:

  • Arduino Uno – US $10
  • Arduino Ethernet Shield – US $10
  • *12V DC to 5V DC Power Supply – US $5
  • USB Cable for Arduino – usually included with Arduino
  • Arduino Uno Case – US $5
  • Starter Kit (LEDs wires, resistors, breadboard) – US $10 – $20
  • Raymarine Rudder Reference Transducer – US $100 on eBay (shop around)
  • Housing for LEDs – be creative, I used a retro wooden quill pen case
  • Cat 5 cable (copper core) – Great for longer wire runs.
  • Software for your Mac or PC – free

*don’t forget a fuse when connecting to your DC system


The rudder reference unit is attached to the rudder using the instructions that come with it from Raymarine. The cable from the rudder reference containing four wires is run to the helm where the Arduino is located. You will need to mount the 5 LEDs in something permanent (to replace the breadboard shown in the photo below) and also terminate the rudder reference there using just the green and blue wires. I mounted the LEDs in a wooden pen case that hinges open and is hollow inside.  Use all red LEDs if you cruise at night!


As we steer the boat and the rudder moves port lock to starboard lock, the LED’s light in sequence with the white LED illuminating when the rudder is exactly centered.  By turning on two adjacent LEDs we can divide turning range into 9 unique positions (see video above). If you login to your wireless network which is broadcast from the router that has the ethernet cable from the Arduino attached and go to a specific IP that you set in the Arduino sketch, you will see the gauge on your web browser screen.  In the sketch example you go to

Important Notes:

  • The sketch uses a fixed IP address not from a DHCP server.  This uses less memory on the Arduino because the DHCP library adds lots of extra code when it is compiled.
  • You will have to change the mac address in the sketch to match the label that ships with your ethernet shield.  Alternatively you can make one up.
  • You will need a micro-SD card and will need to copy the index.htm file to the root directory. If the micro-SD card is not present in the ethernet shield then nothing will happen.  You will need to initialize your SD card by following the arduino ethernet tutorial.
  • Pins 5-9 are used for the LED’s because the ethernet shield internally uses pin 10 (shield) and pin 4 (sd card).

Circuit Diagram: (click to enlarge)

Rudder reference circuit diagram

Arduino Sketch (program):

Description:  Arduino web server web page displays Arduino
analog value on a dial gauge.
The web page is stored on the SD card.
Ajax is used to update the analog value on the
web page.

Hardware:     Arduino Uno and official Arduino Ethernet
shield. Should work with other Arduinos and
compatible Ethernet shields.
2Gb micro SD card formatted FAT16.
Potentiometer interfaced to A2 analog input.

Software:     Developed using Arduino 1.0.5 software
Should be compatible with Arduino 1.0 +
SD card contains web page called index.htm

References:   – WebServer example by David A. Mellis and
modified by Tom Igoe
– SD card examples by David A. Mellis and
Tom Igoe
– Ethernet library documentation:
– SD Card library documentation:
– Gauge from:

Date:         27 March 2013
Modified:     19 June 2013

Author:       W.A. Smith, , Adam Hyde

#include <SPI.h>
#include <Ethernet.h>
#include <SD.h>

// size of buffer used to capture HTTP requests
#define REQ_BUF_SZ   50

// MAC address from Ethernet shield sticker under board
byte macAddr[] = {0xAF, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE}; //change this
IPAddress dnsIP(192, 168, 1, 1); // IP address, may need to change depending on network
IPAddress gatewayIP(192, 168, 1, 1); // IP address, may need to change depending on network
IPAddress subnetIP(255, 255, 255, 0); // subnet IP, may need to change depending on network
IPAddress arduinoIP(192, 168, 1, 250); // IP address, may need to change depending on network
EthernetServer server(80);  // create a server at port 80
File webFile;               // the web page file on the SD card
char HTTP_req[REQ_BUF_SZ] = {0}; // buffered HTTP request stored as null terminated string
char req_index = 0;              // index into HTTP_req buffer

//This is the code to make the arduino function as an ohmmeter and rudder sensor
//This code will display the voltage that drops across rudder sensor //along with its resistance
//use blue and green wires on rudder sensor, other wires not needed
//Blue/Green —– Anti clockwise stop —- 1.66k ohm +/- 10%
//Blue/Green —- Clockwise stop —– 3.3k ohm +/- 10%
//open serial console to see the values
//thanks to how to build an ohm meter tutorial at
//note you will have to adjust the range values for variable R3 depending on the range of your own setup once installed

//int analogPin= 2; (replaced lower in code for setting Analog input to pin 2)
//int raw= 0;
int Vin= 5;
float Vout= 0;

//next line for float R1 should be changed to 4700 if using 4.7k ohm resister, 1000 for 1k ohm resister etc
//this resister is close to the upper range of the resistance for the raymarine Z230 / M88105 Rudder Transducer

float R1= 4700;
float R2= 0;
float R3= 0;
float buffer= 0;

void setup()

pinMode(5, OUTPUT);
pinMode(6, OUTPUT);
pinMode(7, OUTPUT);
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);

Serial.begin(9600);       // for debugging

// initialize SD card
Serial.println(“Initializing SD card…”);
if (!SD.begin(4)) {
Serial.println(“ERROR – SD card initialization failed!”);
return;    // init failed
Serial.println(“SUCCESS – SD card initialized.”);
// check for index.htm file
if (!SD.exists(“index.htm”)) {
Serial.println(“ERROR – Can’t find index.htm file!”);
return;  // can’t find index file
Serial.println(“SUCCESS – Found index.htm file.”);

Ethernet.begin(macAddr, arduinoIP, dnsIP, gatewayIP, subnetIP);  // initialize Ethernet device
server.begin();           // start to listen for clients

void loop()

EthernetClient client = server.available();  // try to get client

if (client) {  // got client?
boolean currentLineIsBlank = true;
while (client.connected()) {
if (client.available()) {   // client data available to read
char c =; // read 1 byte (character) from client
// buffer first part of HTTP request in HTTP_req array (string)
// leave last element in array as 0 to null terminate string (REQ_BUF_SZ – 1)
if (req_index < (REQ_BUF_SZ – 1)) {
HTTP_req[req_index] = c;          // save HTTP request character
// last line of client request is blank and ends with \n
// respond to client only after last line received
if (c == ‘\n’ && currentLineIsBlank) {
// send a standard http response header
client.println(“HTTP/1.1 200 OK”);
// remainder of header follows below, depending on if
// web page or XML page is requested
// Ajax request – send XML file
if (StrContains(HTTP_req, “ajax_inputs”)) {
// send rest of HTTP header
client.println(“Content-Type: text/xml”);
client.println(“Connection: keep-alive”);
// send XML file containing input states
else {  // web page request
// send rest of HTTP header
client.println(“Content-Type: text/html”);
client.println(“Connection: keep-alive”);
// send web page
webFile =“index.htm”);        // open web page file
if (webFile) {
while(webFile.available()) {
client.write(; // send web page to client
// display received HTTP request on serial port
// reset buffer index and all buffer elements to 0
req_index = 0;
StrClear(HTTP_req, REQ_BUF_SZ);
// every line of text received from the client ends with \r\n
if (c == ‘\n’) {
// last character on line of received text
// starting new line with next character read
currentLineIsBlank = true;
else if (c != ‘\r’) {
// a text character was received from client
currentLineIsBlank = false;
} // end if (client.available())
} // end while (client.connected())
delay(1);      // give the web browser time to receive the data
client.stop(); // close the connection
} // end if (client)

// send the XML file containing analog value
void XML_response(EthernetClient cl)

int analog_val;

cl.print(“<?xml version = \”1.0\” ?>”);
// read analog pin A2
analog_val = analogRead(2);
buffer= analog_val * Vin;
Vout= (buffer)/1024.0;
buffer= (Vin/Vout) -1;
R2= -R1 * buffer; //to make guage show lower values clockwise
R3 = -R2;
Serial.print(“Vout: “);
Serial.print(“R3: “);

//make far red show for wheel lock to port
if (R3 > 3200) {
digitalWrite(7, LOW);
digitalWrite(8, LOW);
digitalWrite(5, HIGH);
digitalWrite(6, LOW);
digitalWrite(9, LOW);

//make 2 reds show for next range
if ((R3 < 3201) && (R3 > 3020)) {
digitalWrite(7, LOW);
digitalWrite(8, LOW);
digitalWrite(5, HIGH);
digitalWrite(6, HIGH);
digitalWrite(9, LOW);

//make red show for next range
if ((R3 < 3021) && (R3 > 2840)) {
digitalWrite(7, LOW);
digitalWrite(8, LOW);
digitalWrite(5, LOW);
digitalWrite(6, HIGH);
digitalWrite(9, LOW);

//make white and red show for next range
if ((R3 < 2841) && (R3 > 2669)) {
digitalWrite(7, HIGH);
digitalWrite(8, LOW);
digitalWrite(5, LOW);
digitalWrite(6, HIGH);
digitalWrite(9, LOW);

//make white light show for center range
if ((R3 > 2630) && (R3 < 2670)) {
digitalWrite(7, HIGH);
digitalWrite(5, LOW);
digitalWrite(6, LOW);
digitalWrite(8, LOW);
digitalWrite(9, LOW);

//make white and green show for next range
if ((R3 < 2631) && (R3 > 2470)) {
digitalWrite(7, HIGH);
digitalWrite(8, HIGH);
digitalWrite(5, LOW);
digitalWrite(6, LOW);
digitalWrite(9, LOW);

//make green show for next range
else if ((R3 < 2471) && (R3 > 2300)) {
digitalWrite(7, LOW);
digitalWrite(8, HIGH);
digitalWrite(5, LOW);
digitalWrite(6, LOW);
digitalWrite(9, LOW);

//make 2 green show for next range
if ((R3 < 2301) && (R3 > 2130)) {
digitalWrite(7, LOW);
digitalWrite(8, HIGH);
digitalWrite(5, LOW);
digitalWrite(6, LOW);
digitalWrite(9, HIGH);

//make far green show for wheel lock to starboard
if (R3 < 2131) {
digitalWrite(7, LOW);
digitalWrite(8, LOW);
digitalWrite(5, LOW);
digitalWrite(6, LOW);
digitalWrite(9, HIGH);


// sets every element of str to 0 (clears array)
void StrClear(char *str, char length)
for (int i = 0; i < length; i++) {
str[i] = 0;

// searches for the string sfind in the string str
// returns 1 if string found
// returns 0 if string not found
char StrContains(char *str, char *sfind)
char found = 0;
char index = 0;
char len;

len = strlen(str);

if (strlen(sfind) > len) {
return 0;
while (index < len) {
if (str[index] == sfind[found]) {
if (strlen(sfind) == found) {
return 1;
else {
found = 0;

return 0;


// end

Also download this zipped text file called then unzip it, rename it to index.htm, and copy it to your SD card.