I have a sailboat with a rather old wind instrument package on it. The instrument processor talks to the display instruments using NMEA codes. I recently bought a new autopilot and it accepts NMEA codes and can use them to do things like steer to a relative wind heading. However, the autopilot was expecting a different format than the old instrument package puts out. Thus, I needed to make a translator that could listen to the data going to the instruments, and when it saw the right sentence, convert it and send it back out in the new format.
All that said, there are NMEA multiplexers out there already that will do this (like the one from Brookhouse) but I had wanted to play with something like an Arduino for some time.
Please note that I am NOT a programmer of any sort. I hack at what others have done until it sort of does what I want it to do. So I am sure that there were better ways of doing what I did. Somebody has probably written a library that will do this in three function calls, but there you go. At least I can read my code and figure out what it does.
If you have suggestions for how I could have made this better, please feel free to post improvements or let me know!
/*
Code intended to listen to NMEA data stream and convert any sentance that is in the MWV format to the VWR format
Currently Is: Should Be:
$--VWR,x.x,a,x.x,N,x.x,M,x.x,K*hh $--MWV,x.x,a,x.x,a*hh
1) Wind direction magnitude in degrees 0-180 1) Wind Angle, 0 to 360 degrees
2) Wind direction Left/Right of bow 2) Reference, R = Relative, T = True
3) Speed 3) Wind Speed
4) N = Knots 4) Wind Speed Units, K/M/N
5) Speed 5) Status, A = Data Valid
6) M = Meters Per Second 6) Checksum
7) Speed
8) K = Kilometers Per Hour
9) Checksum
Code written by Paul Carroll, pogcarr at yahoo.com, 4-28-2009
Liberally borrowed from code found on Arduino Playground:
Parallax GPS interpreter by igor.gonzalez.martin@gmail.com 05-04-2007
*/
#include <string.h>
#include <ctype.h>
int ledPin = 13; // LED test pin (insert where needed to test code)
int rxPin = 0; // RX PIN
int txPin = 1; // TX PIN
int byteNMEA=-1; // a byte of the NMEA stream cominig in on serial port
char linea[300] = ""; // a string buffer that we write each byteNMEA to
char HdrStr[5] = "VWR"; // the header of the sentence we are trying to find to convert
char Str1[10] = ""; // string for use in manipulating the wind heading info
char Str2[10] = ""; // string for use in manipulating the wind velocity info
char CSSentence[85] = "" ; //string for sentance assembly so we can checksum it
int count=0;
int check=0;
int counta=0;
int index[10];
int relhdg = 0; // relative heading in degrees
int cs ; //checksum
void setup() {
pinMode(ledPin, OUTPUT); // Initialize LED pin
pinMode(rxPin, INPUT);
pinMode(txPin, OUTPUT);
Serial.begin(4800);
for (int i=0;i<300;i++){ // Initialize a buffer for received data
linea[i]=' ';
}
}
void loop() {
digitalWrite(ledPin, HIGH); // Program got to here (loop began)
byteNMEA=Serial.read(); // Read a byte of the serial port
if (byteNMEA == -1) { // See if the port is empty yet
delay(100);
} else {
linea[counta]=byteNMEA; // If there is serial port data, it is put in the buffer
counta++;
// Serial.print(byteNMEA,BYTE); // comment out to just add in corrected statements, best to hook output of this into input channel of NMEA multiplexer.
// if left in, echo mode if you will, we send each byte on down the serial line. However, I don't think there is anything implemented to prevent crashes with data we will generate, so need to test.
if (byteNMEA==13){ // If the received byte is = to 13 (CR), end of transmission
digitalWrite(ledPin, LOW); // Program found an end of transmission character in datastream
counta=0;
count=0;
check=0;
for (int i=4;i<7;i++){ // Check characters of string to verify if the received command starts with $VWMWV
if (linea[i]==HdrStr[i-4]){
check++;
}
}
if(check==3){ // If we reached 3 it is a match, continue and process the data
for (int j=0;j<strlen(linea);j++){ //now we need to figure out how to parse the string
if (linea[j]==','){ // check for the position of the "," separators
index[count]=j;
count++;
}
if (linea[j]=='*'){ // ... and the "*"
index[8]=j;
count++;
}
}
//Now that we know there the break points in the sentance are, we need to convert the numerical portion to numbers
//Relative heading is between indices0-1 and velocity in knots between indecise2-3
strncpy(Str1,linea+index[0]+1,index[1]-index[0]-1); //copy the section between the indices to Str1
Str1[strlen(Str1)+1]='\0'; // add the pesky \0 to keep C programming language happy
relhdg= atoi(Str1);
if(linea[index[1]+1]=='L'){ //if the hdg indicator is L for measured from left bow, switch it to R
relhdg = 360 - relhdg;
}
itoa(relhdg,Str1,10); //converting integer to ascii text (base 10)
strncpy(Str2,linea+index[2]+1,index[3]-index[2]-1); //copy section between these vertices for Str2 (wind velocity) which we assume is coming in in knots!!!
Str2[strlen(Str2)+1]='\0';
//Now assemble a sentance of the various parts so that we can calculate the proper checksum
strcpy(CSSentence,"PIMWV,"); //The string could be kept the same as what it came in as, but I chose to use the P (prefix for all proprietary devices) and I for instrument
strcat(CSSentence,Str1);
strcat(CSSentence, ".0,R,"); //the atoi command truncated the decimals, might as well stick .0 back on in case something is expecting this field to have a decimal
strcat(CSSentence,Str2);
strcat(CSSentence, ",N,A*"); //Our instruments get the wind velocity in knots, we will be putting it back out in knots
//Now that we have a string fully assembled, we can walk through it and calculate the checksum that will be required for any NMEA sentance
cs=0; //clear any old checksum
for (int n=0; n < strlen(CSSentence); n++) {
cs ^= CSSentence[n]; //calculates the checksum
}
Serial.print('
); // Assemble the final message and send it out the serial port
Serial.print(CSSentence);
Serial.print(cs, HEX);
Serial.println();
strcpy(CSSentence,""); //clears the strings for reuse
strcpy(Str1,"");
strcpy(Str2,"");
strcpy(linea,"");
}
}
}
}