NMEA converter/multiplexer (Arduino Mega)

Hi,
I’m trying to do the following, half working but the other half not and wondering why...
Input: mwv (relative wind) sentence from wind sensor
Input: gprmc sentence from gps
Output: mwv (relative wind) as is ==>working
Output: vwr sentence (converted from mwv) ==>working
Output: mwv (true wind, calculated with gprmc and relative wind)==>not working, seeing only 2 sentences above continuously but no true wind sentence
Confirmed that gprmc is coming out from gps
Below is the code. Appreciate your help and guidance.

Cheers,
z

#include <PString.h>
#include <nmea.h>

NMEA nmeaDecoder(ALL);

// 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;  
 }  

//true wind functions defined below
double calc_twa(double bs, double aws, double awa)
{
   double y = 3.141592 * (90 - awa) / 180;  
   double a = aws * cos(y);
   double bb = aws * sin(y); //MZ: unnecessary?
   double b = bb - bs;
   double twa = 90 - 180 * atan2(b, a) / 3.141592;
   if (twa < 0) twa += 360;
   if (bs == 0 && aws == 0) return awa;
   else return twa;
}
 
double  calc_tws(double bs, double aws, double awa)
{
   double y = 3.141592 * (90 - awa) / 180;
   double a = aws * cos(y);
   double bb = aws * sin(y);
   double b = bb - bs;
   double tws = sqrt((a * a) + (b *b ));  //MZ: originally(b*)??
   return tws;
}


void setup() {

Serial.begin(4800);
Serial1.begin(4800);
Serial3.begin(4800);
}


void loop() 
{
  
  if(Serial1.available())
  {
      
   if (nmeaDecoder.decode(Serial1.read())) 
   {  
      char*title=nmeaDecoder.term(0);  
      if (strcmp(title,"WIMWV")==0) 
      {                                            // only run the following code if the incoming sentence is MWV  
      Serial1.println(nmeaDecoder.sentence());     // prints the original MWV(R)
      Serial.println(nmeaDecoder.sentence());      // Same above. Just to see if this is working with IDE serial monitor
      int awa1=atoi(nmeaDecoder.term(1));          // declares a integer from a string
      float aws1=atof(nmeaDecoder.term(3));        // declares a float from a string
      
      int x;      
       if(awa1>180){int x=180;} 
       else{int x=0;}
       int vdir=awa1-x;                            // convert 360 degrees to 180 for VWR
                        
      float vveln=aws1;                            // wind speed in knots
      float vvelm=aws1*1852/3600;                  // wind speed in m/s
      float vvelk=aws1*1.852;                      // wind speed in kh
                 
      // Time to assemble VWR sentence  
      
      char vwrSentence [36];                       // the VWR sentence can be up to 36 characters long  
      byte cst;  
      PString strt(vwrSentence, sizeof(vwrSentence));  
      strt.print("$XXVWR,");  
      strt.print(vdir);  
      strt.print(",");
      if(awa1>180)                                 // R if 0-180 degrees L if 180~360 degrees  
      {strt.print("L");}
      else{strt.print("R");}
      strt.print(",");  
      strt.print(vveln,1);
      strt.print(",N,");  
      strt.print(vvelm,1);
      strt.print(",M,");  
      strt.print(vvelk,1);
      strt.print(",K*");  
     
      cst = checksum(vwrSentence);  
      if (cst < 0x10) strt.print('0');            // Arduino prints 0x007 as 7, 0x02B as 2B, so we add it now  
      strt.print(cst, HEX);  
      Serial1.println(vwrSentence);               // Output VWR Sentence
      Serial.println(vwrSentence);                // Same above. Just to see if this is working with IDE serial monitor
      

  if(Serial3.available())
  {
    if(nmeaDecoder.decode(Serial3.read()))
    {
      char*title=nmeaDecoder.term(0);
      if(strcmp(title,"GPRMC")==0)
      {
      Serial.println(nmeaDecoder.sentence());     //Output GPRMC to IDE serial monitor to check
      float bs1=atof(nmeaDecoder.term(7));
      double twa1;
      double tws1;
      twa1=calc_twa(bs1, aws1, awa1);
      tws1=calc_tws(bs1, aws1, awa1);
  
     // Time to assemble the MWV(T) sentence  
      char mwvtrueSentence [36];                  // the MWV sentence can be up to 36 characters long  
      byte cst;  
      PString strt(mwvtrueSentence, sizeof(mwvtrueSentence));  
      strt.print("$XXMWV,");  
      strt.print(twa1,0);  
      strt.print(",");
      strt.print("T");
      strt.print(",");  
      strt.print(tws1,1);
      strt.print(",K,A*");  
           
      cst = checksum(mwvtrueSentence);  
      if (cst < 0x10) strt.print('0');            // Arduino prints 0x007 as 7, 0x02B as 2B, so we add it now  
      strt.print(cst, HEX);  
      Serial1.println(mwvtrueSentence);           // Output MWV(T) sentence
      Serial.println(mwvtrueSentence);            // Same above. Just to see if this is working with IDE serial monitor  
        }                                         // } for strcmp
       }                                          // } for nmeadecoder
      }                                           // } for gpsserial
    }
   }
  }
}

I give you credit for posting using the code tags, however, your paste has a lot of distracting formatting tags in it. It's just too hard to read. Please try to fix it.

Thank you very much for pointing this out. I just fixed it in the original post.

Tried below to see if Serial3 is working but still only saw MWV and VWR

#include <PString.h>
#include <nmea.h>

NMEA nmeaDecoder(ALL);

// 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;  
 }  

//true wind functions defined below
double calc_twa(double bs, double aws, double awa)
{
   double y = 3.141592 * (90 - awa) / 180;  
   double a = aws * cos(y);
   double bb = aws * sin(y); //MZ: unnecessary?
   double b = bb - bs;
   double twa = 90 - 180 * atan2(b, a) / 3.141592;
   if (twa < 0) twa += 360;
   if (bs == 0 && aws == 0) return awa;
   else return twa;
}
 
double  calc_tws(double bs, double aws, double awa)
{
   double y = 3.141592 * (90 - awa) / 180;
   double a = aws * cos(y);
   double bb = aws * sin(y);
   double b = bb - bs;
   double tws = sqrt((a * a) + (b *b ));  //MZ: originally(b*)??
   return tws;
}


void setup() {

Serial.begin(4800);
Serial1.begin(4800);
Serial3.begin(4800);
}


void loop() 
{
  
  if(Serial1.available())
  {
      
   if (nmeaDecoder.decode(Serial1.read())) 
   {  
      char*title=nmeaDecoder.term(0);  
      if (strcmp(title,"WIMWV")==0) 
      {                                            // only run the following code if the incoming sentence is MWV  
      Serial1.println(nmeaDecoder.sentence());     // prints the original MWV(R)
      Serial.println(nmeaDecoder.sentence());      // Same above. Just to see if this is working with IDE serial monitor
      int awa1=atoi(nmeaDecoder.term(1));          // declares a integer from a string
      float aws1=atof(nmeaDecoder.term(3));        // declares a float from a string
      
      int x;      
       if(awa1>180){int x=180;} 
       else{int x=0;}
       int vdir=awa1-x;                            // convert 360 degrees to 180 for VWR
                        
      float vveln=aws1;                            // wind speed in knots
      float vvelm=aws1*1852/3600;                  // wind speed in m/s
      float vvelk=aws1*1.852;                      // wind speed in kh
                 
      // Time to assemble VWR sentence  
      
      char vwrSentence [36];                       // the VWR sentence can be up to 36 characters long  
      byte cst;  
      PString strt(vwrSentence, sizeof(vwrSentence));  
      strt.print("$XXVWR,");  
      strt.print(vdir);  
      strt.print(",");
      if(awa1>180)                                 // R if 0-180 degrees L if 180~360 degrees  
      {strt.print("L");}
      else{strt.print("R");}
      strt.print(",");  
      strt.print(vveln,1);
      strt.print(",N,");  
      strt.print(vvelm,1);
      strt.print(",M,");  
      strt.print(vvelk,1);
      strt.print(",K*");  
     
      cst = checksum(vwrSentence);  
      if (cst < 0x10) strt.print('0');            // Arduino prints 0x007 as 7, 0x02B as 2B, so we add it now  
      strt.print(cst, HEX);  
      Serial1.println(vwrSentence);               // Output VWR Sentence
      Serial.println(vwrSentence);                // Same above. Just to see if this is working with IDE serial monitor
      

  if(Serial3.available())
  {
    if(nmeaDecoder.decode(Serial3.read()))
    {
      char*title=nmeaDecoder.term(0);
      if(strcmp(title,"GPRMC")==0)
      {
      Serial.println(nmeaDecoder.sentence());     //Output GPRMC to IDE serial monitor to check
        }                                         
       }                                          
      }                                           
    }
   }
  }
}

Then tried this and saw GPRMC coming out in serial monitor... So serial 3 itself seems working.

#include <PString.h>
#include <nmea.h>

NMEA nmeaDecoder(ALL);

// 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;  
 }  

//true wind functions defined below
double calc_twa(double bs, double aws, double awa)
{
   double y = 3.141592 * (90 - awa) / 180;  
   double a = aws * cos(y);
   double bb = aws * sin(y); //MZ: unnecessary?
   double b = bb - bs;
   double twa = 90 - 180 * atan2(b, a) / 3.141592;
   if (twa < 0) twa += 360;
   if (bs == 0 && aws == 0) return awa;
   else return twa;
}
 
double  calc_tws(double bs, double aws, double awa)
{
   double y = 3.141592 * (90 - awa) / 180;
   double a = aws * cos(y);
   double bb = aws * sin(y);
   double b = bb - bs;
   double tws = sqrt((a * a) + (b *b ));  //MZ: originally(b*)??
   return tws;
}


void setup() {

Serial.begin(4800);
Serial1.begin(4800);
Serial3.begin(4800);
}


void loop() 
{
  

  if(Serial3.available())
  {
    if(nmeaDecoder.decode(Serial3.read()))
    {
      char*title=nmeaDecoder.term(0);
      if(strcmp(title,"GPRMC")==0)
      {
      Serial.println(nmeaDecoder.sentence());     //Output GPRMC to IDE serial monitor to check
        }                                         
       }                                          
      }                                           
    }

I don't understand your setup, do you have two devices emiting NMEA sentences?

If so, I note that the reading from serial3 only takes place if something was read on serial1 first. Was that deliberate?

Yes, I got chart plotter sending gprmc and wind sensor sending mwv(relative wind). As noted above mwv is read on serial 1 but gprmc is not on serial3.

Actually that was not deliberate but mwv (true wind) calc is dependent on mwv (relative wind) reading on serial 1 anyway

zaizen:
Actually that was not deliberate but mwv (true wind) calc is dependent on mwv (relative wind) reading on serial 1 anyway

Maybe so, I'd make them independent anyway, at least as far as reading the serial ports are concerned.

Got it. Changed the code so serials are reading independently. Will test next time go to boat. Let you know the result. Thanks,

It worked! Thanks a lot! The code below is one worked although it turned out that the formula to convert relative wind speed into true wind speed is wrong... I'm going to fix that as well. Thanks again.

#include <PString.h>
#include <nmea.h>

NMEA nmeaDecoder(ALL);

// 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;  
 }  

//true wind functions defined below
double calc_twa(double bs, double aws, double awa)
{
   double y = 3.141592 * (90 - awa) / 180;  
   double a = aws * cos(y);
   double bb = aws * sin(y); //MZ: unnecessary?
   double b = bb - bs;
   double twa = 90 - 180 * atan2(b, a) / 3.141592;
   if (twa < 0) twa += 360;
   if (bs == 0 && aws == 0) return awa;
   else return twa;
}
 
double  calc_tws(double bs, double aws, double awa)
{
   double y = 3.141592 * (90 - awa) / 180;
   double a = aws * cos(y);
   double bb = aws * sin(y);
   double b = bb - bs;
   double tws = sqrt((a * a) + (b *b ));  
   return tws;
}

//shared variables
double awa1;
double aws1;

void setup() {

Serial.begin(4800);
Serial1.begin(4800);
Serial3.begin(4800);
}


void loop() 
{
  //MWV(R) from wind sensor to parse VWR for raymarine tiller pilot and parse MWV(R) as is
  if(Serial1.available())
  {
    
   if (nmeaDecoder.decode(Serial1.read())) 
   {  
      char*title=nmeaDecoder.term(0);  
      if (strcmp(title,"WIMWV")==0) 
      {                                            // only run the following code if the incoming sentence is MWV  
      Serial1.println(nmeaDecoder.sentence());     // prints the original MWV(R)
      Serial.println(nmeaDecoder.sentence());      // Same above. Just to see if this is working with IDE serial monitor
      awa1=atoi(nmeaDecoder.term(1));              // declares a integer from a string
      aws1=atof(nmeaDecoder.term(3));              // declares a float from a string
      
      int x;      
       if(awa1>180){int x=180;} 
       else{int x=0;}
       int vdir=awa1-x;                            // convert 360 degrees to 180 for VWR
                        
      float vveln=aws1;                            // wind speed in knots
      float vvelm=aws1*1852/3600;                  // wind speed in m/s
      float vvelk=aws1*1.852;                      // wind speed in kh
                 
      // Time to assemble VWR sentence  
      
      char vwrSentence [36];                       // the VWR sentence can be up to 36 characters long  
      byte cst;  
      PString strt(vwrSentence, sizeof(vwrSentence));  
      strt.print("$XXVWR,");  
      strt.print(vdir);  
      strt.print(",");
      if(awa1>180)                                 // R if 0-180 degrees L if 180~360 degrees  
      {strt.print("L");}
      else{strt.print("R");}
      strt.print(",");  
      strt.print(vveln,1);
      strt.print(",N,");  
      strt.print(vvelm,1);
      strt.print(",M,");  
      strt.print(vvelk,1);
      strt.print(",K*");  
     
      cst = checksum(vwrSentence);  
      if (cst < 0x10) strt.print('0');            // Arduino prints 0x007 as 7, 0x02B as 2B, so we add it now  
      strt.print(cst, HEX);  
      Serial1.println(vwrSentence);               // Output VWR Sentence
      Serial.println(vwrSentence);                // Same above. Just to see if this is working with IDE serial monitor
      }                                           // } for strcmp
      awa1=atoi(nmeaDecoder.term(1));             // awa1 as a shared variable
      aws1=atof(nmeaDecoder.term(3));             // aws1 as a shared variable
   }                                              // } for nmeaDecoder
  }                                               // } for Serial1 available
  // END of VWR


  //GPRMC from NMEA Multiplexer to parse MWV(T)
  if(Serial3.available())
  {
    if(nmeaDecoder.decode(Serial3.read()))
    {
      char*title=nmeaDecoder.term(0);
      if(strcmp(title,"GPRMC")==0)
      {
      Serial.println(nmeaDecoder.sentence());     //Output GPRMC to IDE serial monitor to check
      double bs1=atof(nmeaDecoder.term(7));
      double twa1;
      double tws1;
      twa1=calc_twa(bs1, aws1, awa1);
      tws1=calc_tws(bs1, aws1, awa1);
  
     // Time to assemble the MWV(T) sentence  
      char mwvtrueSentence [36];                  // the MWV sentence can be up to 36 characters long  
      byte cst;  
      PString strt(mwvtrueSentence, sizeof(mwvtrueSentence));  
      strt.print("$XXMWV,");  
      strt.print(twa1,0);  
      strt.print(",");
      strt.print("T");
      strt.print(",");  
      strt.print(tws1,1);
      strt.print(",K,A*");  
           
      cst = checksum(mwvtrueSentence);  
      if (cst < 0x10) strt.print('0');            // Arduino prints 0x007 as 7, 0x02B as 2B, so we add it now  
      strt.print(cst, HEX);  
      Serial1.println(mwvtrueSentence);           // Output MWV(T) sentence
      Serial.println(mwvtrueSentence);            // Same above. Just to see if this is working with IDE serial monitor  
        }                                         // } for strcmp
       }                                          // } for nmeadecoder
      }                                           // } for Serial3 available
  //End of MWV(T)
    }