Ultrasonic anemometer reading

Hi,

For my bachelor thesis I have one part where I need to measure wind speed and direction. For this I will be using CV7-F from LCJ capteurs: http://www.lcjcapteurs.com/CV7_3.htm

This will give me an output based on the NEMA0183 protocol.
For example: $IIMWV,225.0,R,000.0,N,A *38
$: start
IIMWV: wind speed and direction
225.0: wind direction
R: relative reference
000.0: wind speed
N: unit (knots)
A: available
*38: checksum

Now I don't have the sensor yet so I made a program that will sent the string of information to another arduino.

void setup()
{
Serial.begin(9600);
Serial2.begin(9600);
}

void loop()
{
  String anemometerString = "$IIMWV,225.0,R,000.0,N,A *38";
  Serial.println(anemometerString);
  Serial2.write ("$IIMWV,225.0,R,000.0,N,A *38");
  delay(500);
}

On the receiving arduino I've put a program to retrieve the information. There is where my problem starts. I can retrieve the information as separated characters. So on the monitor they just show up after each other. This is the program I made for it.

char anemometerIn = 0;
String anemometerString;

void setup()
{
 Serial.begin(9600);
 Serial2.begin(9600);
 Serial.println("power on");
}

void loop()
{ 
 if (Serial2.available() > 0)
 {
   anemometerIn = Serial2.read();
   Serial.println(anemometerIn);
 }
 
}

Now for getting all the information out of it I was thinking to get it back into a string. I started searching, but I don't get it how it works. I've tried it, but I got overflows or I got a string that was reading every char 2 times...

I'm totally new to this, only have been programming for 2-3 weeks now. Sorry for the maybe very easy question.

I noticed it is a popular subject these days in schools ...

backgrounder - http://www.technik.dhbw-ravensburg.de/~lau/ultrasonic-anemometer.html -

Receiver

char data[40];
int idx = 0;

void setup()
{
 Serial.begin(9600);
 Serial2.begin(9600);
 Serial.println("power on");
}

void loop()
{ 
  if (Serial2.available() > 0)
  {
    c = Serial2.read();
    data[idx++] = c;
    if (c == '\n') // end of line
    {
      data[idx]  = 0; // string terminator
      processData();
      idx = 0;
    }
  }
}

void processData()
{
  // split the char array in fields (hint use strtok() 
  // and process them e.g. printing  
}

Where I am at the moment I get this as result:

$IIMWV,225.0,R,000.0,N,A *38$IIMWV,225.0,
$IIMWV,225.0,R,000.0,N,A *38$IIMWV,225.0R
$IIMWV,225.0,R,000.0,N,A *38$IIMWV,225.0,
$IIMWV,225.0,R,000.0,N,A *38$IIMWV,225.00
$IIMWV,225.0,R,000.0,N,A *38$IIMWV,225.00
$IIMWV,225.0,R,000.0,N,A *38$IIMWV,225.00
$IIMWV,225.0,R,000.0,N,A *38$IIMWV,225.0.
$IIMWV,225.0,R,000.0,N,A *38$IIMWV,225.00
$IIMWV,225.0,R,000.0,N,A *38$IIMWV,225.0,
$IIMWV,225.0,R,000.0,N,A *38$IIMWV,225.0N
$IIMWV,225.0,R,000.0,N,A *38$IIMWV,225.0,
$IIMWV,225.0,R,000.0,N,A *38$IIMWV,225.0A
$IIMWV,225.0,R,000.0,N,A *38$IIMWV,225.0 
$IIMWV,225.0,R,000.0,N,A *38$IIMWV,225.0*
$IIMWV,225.0,R,000.0,N,A *38$IIMWV,225.03
$IIMWV,225.0,R,000.0,N,A *38$IIMWV,225.08
$IIMWV,225.0,R,000.0,N,A *38$IIMWV,225.0$
$IIMWV,225.0,R,000.0,N,A *38$IIMWV,225.0I
$IIMWV,225.0,R,000.0,N,A *38$IIMWV,225.0I
$IIMWV,225.0,R,000.0,N,A *38$IIMWV,225.0M
$IIMWV,225.0,R,000.0,N,A *38$IIMWV,225.0W
$IIMWV,225.0,R,000.0,N,A *38$IIMWV,225.0V
$IIMWV,225.0,R,000.0,N,A *38$IIMWV,225.0,
$IIMWV,225.0,R,000.0,N,A *38$IIMWV,225.02
$IIMWV,225.0,R,000.0,N,A *38$IIMWV,225.02

Etc... As you see, there are no endings in my strings, did I do something wrong with the transmitter program where I just sent a sting? Because nowhere in that string I have a '\n' . But I look at that to make my strings at the receiver. Or am I totally wrong ?

As you see, there are no endings in my strings

Really? Then how did each $ end up on a new line? The sender appears to be sending a carriage return/line feed that ends a packet.

PaulS:

As you see, there are no endings in my strings

Really? Then how did each $ end up on a new line? The sender appears to be sending a carriage return/line feed that ends a packet.

Ok, true. But why do I always get half of a string after 1 total string. $IIMWV,225.0,R,000.0,N,A *38$IIMWV,225.00
The green is my string that I sent, and the red is a part of the next string I think. So where in my sending program is the decision made where to stop sending it? Or does it just keeps on sending the same string until my delay is over and than sent it, even if the other is half, again from the beginning?

But why do I always get half of a string after 1 total string. $IIMWV,225.0,R,000.0,N,A *38$IIMWV,225.00
The green is my string that I sent, and the red is a part of the next string I think. So where in my sending program is the decision made where to stop sending it? Or does it just keeps on sending the same string until my delay is over and than sent it, even if the other is half, again from the beginning?

I don’t know what code you are now running on the sender, or what code you are now running on the receiver, so I can’t answer your question.

My sender is the program I put in my opening post.

My receiver now looks like this:

#include <string.h>

char data[40];
int dataStream;
int index = 0;
char *str = 0;
char *pointer = 0;

void setup()
{
 Serial.begin(9600);
 Serial2.begin(9600);
 Serial.println("power on");
}

void loop()
{ 
  while (Serial2.available() > 0)
  {
    dataStream = Serial2.read();
    
    if (dataStream == '\n') // end of line
    {
      index = 0;
      data[index]  = 0; // string terminator
      processData();
    }
    else 
    {
      data[index] = dataStream;
      index++;
    }
      Serial.println(data);
    }
}

void processData()
{
  while ((str = strtok_r(pointer, ",", &pointer)) != "\n") // delimiter is the comma
  {
   Serial.println(str);
  } 
   pointer = NULL;
}

The void processData is just something that isn’t working yet. I still need to figure out how this all works. I’m pretty new to programming so I have still a lot to learn…

$IIMWV,225.0,R,000.0,N,A *38$IIMWV,225.0A

your output looks this way probably because the receiving char array is 40 bytes long and the lines too

There is a bug in your code (you did't copy very well :wink:

your copy

    if (dataStream == '\n') // end of line
    {
      index = 0;
      data[index]  = 0; // string terminator
      processData();
    }

my original

    if (c == '\n') // end of line
    {
      data[idx]  = 0; // string terminator
      processData();
      idx = 0;
    }

do you notice the fundamental difference?

The void processData is just something that isn't working yet.

Isn't working? Or, isn't called?

I'm not sure why you feel it necessary to print data each time you read a new character. data is not properly NULL terminated unless a carriage return arrives, so most of the time, data is not a valid array to pass to a function that expects a string (a NULL terminated array of chars).

You need to add

data[index] = '\0';

after

      data[index] = dataStream;
      index++;

dataStream is a lousy name for something that is not a stream, by the way.

Finally, strtok_r() is the thread-safe, and far more complex, version of strtok(). Since the Arduino is not multi-threaded, the advantages of using a thread-safe function escape me, while the disadvantages are obvious - larger code and far more variables to keep track of.

robtillaart:

$IIMWV,225.0,R,000.0,N,A *38$IIMWV,225.0A

your output looks this way probably because the receiving char array is 40 bytes long and the lines too

There is a bug in your code (you did't copy very well :wink:

your copy

my original

do you notice the fundamental difference?

-I tried to put the data [28] but this didn't made any difference for some reason, that's why I put it back to 40.

-The bad copy is because your way wasn't very clear for me, and I found this one on the arduino forum. And this I did understand more. But I guess I don't get the fundamental difference than.. It would be nice if you maybe can explain it a little for me :blush:

@ PaulS:
-It's possible that it isn't called because I actually didn't knew it was possible to make a void that way.

  • For the NULL terminated part, I don't know if I'm getting it right? So the '\0' tells that this is the end of the string, right? And with my 0 I'm just doing something foolish?
    The data isn't a string at that moment? Because I thought, what I was doing is getting every character and putting if behind each other and therefor make a string? (correct me if I'm wrong)
  • I changed dataStream to SerialInput. This maybe fits better.
    -I used the strtok_r() because when I searched how the strtok() function worked, I always ended up with the strtok_r() function. What I got from it was that you can't overwrite the data in a memory with the strtok_r(), and with the normal strtok() you could. So I think this wasn't a good idea after all. The multi-threaded thing, I don't understand it yet. I'll search about it so that I can follow you.

I'm very sorry for these noobisch questions, but I can't help it that I need to do this for my bachlor thesis. I just hope I learn something about it because it looks very interesting atm. Even with this little knowledge that I got.

If there are other, better options to get the information from the received string, you can always tell, I don't know if this is the best way.

if you maybe can explain it a little for me

The fundamental difference is that I set the last element of the array with index idx to zero to get a proper zero terminated char array. That is the way C likes it.

You set the index to 0 and then set the first element of the array to zero. This causes problems in the strtok() code as it sees in essence an empty string ...(

Give it a try ...

Ok, I remade it to the things you teached me and put some comments at every line so that you see what I think it does.

char data[27];
int SerialInput;
int index = 0;

void setup()
{
 Serial.begin(9600);
 Serial2.begin(9600);
 Serial.println("power on");
}

void loop()
{ 
  if (Serial2.available() > 0)
  {
    SerialInput = Serial2.read();  //Give a name to the input characters on RX2
    data[index]  = SerialInput;  //Put the characters into a string each character placed 1 position behind the previous one
    index++;
    
    if (SerialInput == '\n')   //If the SerialInput has ended his line
    {
      data[index] = 0;  //give the string a 0 on the end so it know it has ended
      processData();  // start void processData??
      index = 0;  // Let the index start from 0 again
    }
  }
 if (index == 27)  // if the total string is placed, print the string
 {
   Serial.println(data);
 }
}

As result I only get 2 lines... After that it stops for some reason. If I remove my last If-loop, the data will be printed every time something changes. Therefore I get a lot of lines, but with a lot of wrong characters. They appear random in my strings.
The last character with the program I have right now is wrong, I get a 3 in stead of 8 (What I send him)

Again, thanks for all the help so far! Really nice people.
My questions:
-I am making a string right? Not an array, because I'm getting more confused every minute.
-Is data[index++] the same as data[index] and on the next line index++ Because once I got a different result... But after I uploaded it again this was ok.
-Is saying data[index] = 0 the same as data[index] = '\0' ? Or is the 0 character automatically seen as the carriage return like PaulS told me to use?

A string is an array of chars THAT IS NULL TERMINATED.

Just because you have an array that contains characters is not enough to make it a string. The NULL terminator is required.

Rob's code is correct in that it does add a NULL terminator, before expecting it to be used as a string, but I don't like it because the array is not always a string, so you can't print it as a string at any time. I prefer, and it doesn't take that much extra time, to always add a NULL after each character, so that the char array is always a string.

@PaulS
you are right, keeping the char array correctly terminated is far more robust.

Okay, everything works now. Thanks for the advice!

Now I have another problem. I would like to seperate the parts, I did it by the strtok() like you guys told me, but how is it possible to get everything like:

var1 = $IIMWV
var2 = 225.0
var3 = R
etc
(this is the string: $IIMWV,225.0,R,000.0,N,A*38)

I can have it printed this way, but doesn’t the data get’s overwrited every cycle?
This is the program I made to test it, so I don’t mess up my main one.

#include <string.h>

char *data;
char delimiters[] = ",";

void setup()
{
Serial.begin(9600);
}

void loop()
{
char anemometer[] = "$IIMWV,225.0,R,000.0,N,A*38";

data = strtok(anemometer, delimiters);

while (data != NULL)
{
  delay(500);
  Serial.println(data);
  data = strtok(NULL, delimiters); 
}
delay(1000);
}

my goal is to work like this:
if the start of the string = var1 ($IIMWV)
write var2(225.0) to somewhere, var3(R) somewhere, etc
This way I can compare the vars, and make decisions whether it is correct, or use them to calculate other things.

I also tried out the atoi function to get the int’s out of it, but here I have the same problem. How can I write them away so that I can process it?

I will post the main program, the one that is working, later when I get back home.

Already thanks for all the useful advice!

Ben

my goal is to work like this:
if the start of the string = var1 ($IIMWV)

if(strcmp(data, "$IIMWV") == 0)
{
}

write var2(225.0) to somewhere, var3(R) somewhere, etc

data = strtok(NULL, delimiters);
float rVal = atof(data);

data = strtok(NULL, delimiters);
strcpy(var3, data);

I also tried out the atoi function to get the int's out of it

225.0 is not an int. It is a float.

How can I write them away so that I can process it?

That depends on how long you need to persist the values.

Thanks, I did just stumbled upon something likewise and tried it:

#include <string.h>

char *data;
char delimiters[] = ",";
char *a;
char *b;
char *c;
char *d;

void setup()
{
Serial.begin(9600);
}

void loop()
{
char anemometer[] = "$IIMWV,225.0,R,000.0,N,A*38";

a = strtok(anemometer, delimiters);
b = strtok(NULL, delimiters);
c = strtok(NULL, delimiters);
d = strtok(NULL, delimiters);

{
  delay(500);
  Serial.println(a);
  Serial.println(b);
  Serial.println(c);
  Serial.println(d);
    
  data = strtok(NULL, delimiters); 
}
delay(1000);
}

But I will try it your way (it looks quite the same). And now I finally understand the strtok function totally. And indeed, I did meant float :blush:, srry about that.
Thanks again!

Okay, atm I have almost whole the program finished for reading in the values and printing out what needs to be visualized.
The only problem I still have is the checksum. This is the problem:

I have the string: $IIMWV,225.0,R,000.0,N,A*38
The 38 is the bitwise XOR function of every character between $ and *
I stored that solution into an int called CheckSum. If I print the the CheckSum like → Serial.println(CheckSum, HEX);
I will get the 38.

The real problem is, how can I compare the 38 that I have like 2 characters from the string (With the atoi function I can make it into an int) with the CheckSum which is still an int (HEX 38 = int 56)? Because there isn’t an atoh function (I think?), how is it possible to compare these?

I hope you understand what I mean.

The program: (probably not the best solution to everything, like the bitwise XOR. But it works :stuck_out_tongue:

#include <string.h>

// Reading String
char InputChar = 0;
char AnemometerString[27];
int index = 0;

//Dividing String
char Delimiters[] = "$,*";
char *StringPart1;
char *StringPart2;
char *StringPart3;
char *StringPart4;
char *StringPart5;
char *StringPart6;
char *StringPart7;
int CheckSum = 0;

//Processing String Parts
float WindAngle = 0;
float WindSpeed = 0;
char *RelativeReference = "R";
char *True = "T";
char *Knots = "N";
char *KMH = "K";
char *MPH = "M";
char *Available = "A";
char *Alarm = "V";

void setup()
{
Serial2.begin(9600);
Serial.begin(9600);
Serial.println("Power on");
}

void loop()
{
  if (Serial2.available() > 0)  // If the serial input gets information => do the next
  {
    InputChar = Serial2.read();
    AnemometerString[index] = InputChar;
    index++;
    AnemometerString[index] = '\0';    
 
     if ( InputChar == '\n')
    {
      AnemometerString[index] = '\0';	
    }  	 

    if (index == 27)
    {
      
     int CheckSum = AnemometerString[1] ^ AnemometerString[2] ^ AnemometerString[3] ^ AnemometerString[4] ^ AnemometerString[5] ^ AnemometerString[6] ^ AnemometerString[7] ^ AnemometerString[8] ^ AnemometerString[9] ^ AnemometerString[10] ^ AnemometerString[11] ^
                    AnemometerString[12] ^ AnemometerString[13] ^ AnemometerString[14] ^ AnemometerString[15] ^ AnemometerString[16] ^ AnemometerString[17] ^ AnemometerString[18] ^ AnemometerString[19] ^ AnemometerString[20] ^ AnemometerString[21] ^ AnemometerString[22] ^
                    AnemometerString[23];
      
      Serial.print("The whole string: \t");
      Serial.print(AnemometerString);
      Serial.println("\n");
	  
      StringPart1 = strtok(AnemometerString, Delimiters);
      StringPart2 = strtok(NULL, Delimiters);
      StringPart3 = strtok(NULL, Delimiters);
      StringPart4 = strtok(NULL, Delimiters);
      StringPart5 = strtok(NULL, Delimiters);
      StringPart6 = strtok(NULL, Delimiters);
      StringPart7 = strtok(NULL, Delimiters);
      
      int CheckSum2 = atoi(StringPart7);
      
      if (CheckSum == CheckSum2)
      {
  
      if (strcmp (StringPart1, "IIMWV") == 0)
      {
        Serial.print("The start sentence is: \t");
        Serial.println(StringPart1);
        
        WindAngle = atof(StringPart2);
        WindSpeed = atof(StringPart4);
        
        if (strcmp(StringPart3, RelativeReference) == 0)
        {
          Serial.print("The windangle is: \t");
          Serial.print(WindAngle);
          Serial.println(" Relative");
        }
        else if (strcmp(StringPart3, True) == 0)
        {
          Serial.print("The windangle is: \t");
          Serial.print(WindAngle);
          Serial.println(" true");
        }
        else
        {
          Serial.println("Wrong windangle signal");
        }
 
        if (strcmp(StringPart5, Knots) == 0)
        {
          Serial.print("The windspeed is: \t");
          Serial.print(WindSpeed);
          Serial.println(" knots");
        }
        else if (strcmp(StringPart5, KMH) == 0)
        {
          Serial.print("The windspeed is: \t");
          Serial.print(WindSpeed);
          Serial.println(" km/h");
        }
        else if (strcmp(StringPart5, MPH) == 0)
        {
          Serial.print("The windspeed is: \t");
          Serial.print(WindSpeed);
          Serial.println(" mph");
        }
        else 
        {
          Serial.println("Wrong windspeed signal");
        }
        
        if (strcmp(StringPart6, Available) == 0)
        {
        Serial.print("Total string: \t\t");
        Serial.println("Available");
        }
        else if (strcmp(StringPart6, Alarm) == 0)
        {
          Serial.print("The total string: \t\t");
          Serial.println("Alarm!");
        }
        else
        {
          Serial.println("Wrong status!");
        }
        
        Serial.print("The checksum is: \t");
        Serial.println(StringPart7);                                                        
        Serial.println("\n");	
      }
      else
      {
        Serial.println("Error, wrong input device \n");
      }
      }
      
      index = 0;
      
    }
 } 
}

The real problem is, how can I compare the 38 that I have like 2 characters from the string

Converting the string "38" to an int, using atoi() won't help because the string is assumed to be in base 10, not base 16.

You need to convert each character to a int yourself, knowing that it is base 16. If the character is between '0' and '9', the value is the character minus '0' ('3' - '0' = 3). If the character is between 'A' and 'F', the value is the character minus 'A' plus 10 ('D' - 'A' + 10 = 14). Then, multiply the first value by 16 and add the second value.

Then, you can compare that value to the value in CheckSum.