Pages: [1] 2   Go Down
Author Topic: Vaisala WXT520 weather sensor  (Read 2845 times)
0 Members and 1 Guest are viewing this topic.
Serbia
Offline Offline
Newbie
*
Karma: 0
Posts: 13
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I'm new to Arduino, and eager to learn but there is too many information regarding same problems and I'm simply lost.
I would like to make a RS232 to SD data logger but I'm stuck when it comes to dealing with serial communication.
I'm aware that it has already been discussed and there are good tutorials (even already built loggers) but I would like to make it from scratch and get into this amazing Arduino world.
I understand "Serial" and Serial monitor, and I've been practicing with it, but can't figure out the best way to solve the following:
1. Send "0R0+CR+LF" via SoftwareSerial
2. Capture the response from device via SoftwareSerial and display it on Serial monitor (in the code comments, you'll see how the response looks like).
This is just a beginning, later I plan to write received string on SD card (just pass it as it is, without parsing it as it is already CSV).
I've built a RS232 shifter and it is working when I connect Arduino to another PC running terminal application (I can chat using Serial monitor and PC2).
 
Code:
/*Arduino Uno logger for Vaisala WXT520 weather sensor*/

#include <SoftwareSerial.h>

SoftwareSerial vaisala(2, 3); // RX, TX

String command = "0R0"; //request data from sensor, must be followed with CR+LF
String data = ""; // maybe change this approach and not use String

void setup()  
{
  vaisala.begin(19200); // default device settings are 19200,8,N,1
}

void loop()
{  
  vaisala.println(command); //println because CR+LF must be sent

//device will return the following string:
//  0R0,Dx=005D,Sx=2.8M,Ta=23.0C,Ua=30.0P,Pa=1028.2H,Rc=0.00M,Rd=10s,Th=23.6C<cr><lf>
/* capture whole received string and print it to Serial Monitor */
 if (vaisala.available()>0)
    data = vaisala.read();
  
  Serial.println(data);
  delay(2000); //
}

Unfortunately, I can't test this sketch because I can't properly communicate with the mentioned device but that is another issue I'll try to solve myself (I think built in RS232 port on device is broken, so I'll try to use RS485 but I'm waiting RS485/232 converter to arrive).
Regarding the device response given in the code comment, what is the best approach to deal with it?
Thanks in advance!!!
« Last Edit: October 10, 2012, 03:42:32 pm by fermevc » Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 601
Posts: 48543
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
but can't figure out the best way to solve the following:
1. Send "0R0+CR+LF" via SoftwareSerial
What you are doing looks reasonable, except for using String.

char *command = "0R0"; //request data from sensor, must be followed with CR+LF
  vaisala.println(command); //println because CR+LF must be sent

Quote
2. Capture the response from device via SoftwareSerial and display it on Serial monitor (in the code comments, you'll see how the response looks like).
Do you need to send that command EVERY pass through loop? Every time you need to access data? Or once?

Code:
if (vaisala.available()>0)
    data = vaisala.read();
You said that you understood Serial. This doesn't look like it. The Serial.read() function returns one character. Storing that one character as the only data in a String instance is silly. Appending it to the String instance might make sense, but storing the data in a char array (and adding a NULL after adding each character) would make more sense.

But, you need to understand when to stop reading/storing data for one record and when to start reading/storing for the next record.

Logged

Serbia
Offline Offline
Newbie
*
Karma: 0
Posts: 13
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks for fast response!

1. I will take a look what is a difference using char and pointer (*), thanks for advice.
2. Yes, I need to send "request command" every time when I want to receive data, so I think it's OK to do it in a loop()

About my understanding of Serial: I was trying to explain that I know how to use Serial monitor and basic operations (read/write), I don't have a good knowledge regarding Serial class  smiley-red

I think that each response from device is finished with CR+LF (but I'll confirm this when my RS485 converter arrives), so I'll try to take that to stop reading the "vaisala" port. The biggest problem for me is to understand how to construct "char array" and check for "CR+LF". As I said, I've followed a couple of tutorials regarding this, but still can't get it into my head.

Thanks for all the help!
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 601
Posts: 48543
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

This code will read, and store all the serial data between start and end of packet markers:
Code:
#define SOP '<'
#define EOP '>'

bool started = false;
bool ended = false;

char inData[80];
byte index;

void setup()
{
   Serial.begin(57600);
   // Other stuff...
}

void loop()
{
  // Read all serial data available, as fast as possible
  while(Serial.available() > 0)
  {
    char inChar = Serial.read();
    if(inChar == SOP)
    {
       index = 0;
       inData[index] = '\0';
       started = true;
       ended = false;
    }
    else if(inChar == EOP)
    {
       ended = true;
       break;
    }
    else
    {
      if(index < 79)
      {
        inData[index] = inChar;
        index++;
        inData[index] = '\0';
      }
    }
  }

  // We are here either because all pending serial
  // data has been read OR because an end of
  // packet marker arrived. Which is it?
  if(started && ended)
  {
    // The end of packet marker arrived. Process the packet

    // Reset for the next packet
    started = false;
    ended = false;
    index = 0;
    inData[index] = '\0';
  }
}
You can remove all the SOP stuff, since you don't have a start-of-packet marker. Set started to true when you send the 0R0 command.

Where it says "The end of packet marker arrived. Process the packet", you can store the data in inData to the SD card. Make sure that inData is sized appropriately for your amount of data, and that the limit values are adjusted appropriately.
Logged

Serbia
Offline Offline
Newbie
*
Karma: 0
Posts: 13
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

PaulS, thank you for the example code!
I'm starting to get the logic for populating char array, but still can't resolve a couple of things.

1. I'm assuming that every response from device has CR+LF so I should use that information to finish reading the serial buffer, but I don't know the proper way to translate this into code.
In your example you're defining '>' for EOP. What should I use instead of > to detect CR?
a) '\0'
b) 13
c) something else

2. Why are you defining a BYTE for index?
I can understand the counter approach but I've always used an INT for this, so I'm confused.

3. Can you please explain the use of '\0'? I know that it is treated as byte an it is used for ending a string.

Thanks for your effort to help me!
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 601
Posts: 48543
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
What should I use instead of > to detect CR?
'\n'

Quote
2. Why are you defining a BYTE for index?
It can hold 255 different values. More than enough to walk an 80 element array. If you array is larger than that, use an int type variable for the index.

Quote
3. Can you please explain the use of '\0'? I know that it is treated as byte an it is used for ending a string.
It is a NULL. A string is a NULL terminated array of chars. inData is an array of chars. It needs to be NULL terminated to qualify as a string, so that it can be passed to functions that expect a string.
Logged

Serbia
Offline Offline
Newbie
*
Karma: 0
Posts: 13
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

1,2 and 3 perfectly CLEAR, thanks Paul!
I'll try to create a test sketch to verify all you mentioned.
Since I can't try it on actual Vaisala sensor, I'll use a terminal on another PC and route typed text to Serial monitor.
If everything is OK with a sketch, I shouldn't see any data in serial monitor until I hit ENTER on PC2.

Logged

Serbia
Offline Offline
Newbie
*
Karma: 0
Posts: 13
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Finally, I have my weather sensor operational. I can send and receive the data using terminal programs.
Unfortunately, my sketch is not working as I would like. I can send a "command" to Vaisala, but the response is not received properly.
I'm sure that Vaisala is sending the requested data because I have a separate "service connection" active and there is a "Vaisala monitor utility" where I can see all serial messages received and sent by Vaisala device.
I've tried to make some kind of debugging with LCD and Serial monitor, but got lost in the code, so I removed all these attempts.
Here is my sketch, so any suggestions are more than welcome:
Code:
#include <SPI.h>
#include <Wire.h>
#include "RTClib.h"
#include <SD.h>
#include <SoftwareSerial.h>
#include <LiquidCrystal.h>

/* vaisala stuff */
#define EOD '\n' // EOD - end of data marker
boolean ended = false; // using for condition
char *command = "0XU"; //string to request data from sensor
char inData[128]; // char array for storing received bytes
byte index; // byte type is enough for storing received string
SoftwareSerial vaisala(2, 3); // RX, TX
RTC_DS1307 RTC;
char filename[] = "00000000.TXT";
const int chipSelect = 4;      // CS for SD card
LiquidCrystal lcd(14,15,5,6,7,8); // RS,EN,D4,D5,D6,D7
 
void setup()
{
  pinMode(14, OUTPUT); // RS for LCD, Ethernet Shield present, there's not enough digital pins
  pinMode(15, OUTPUT); // EN for LCD, Ethernet Shield present, there's not enough digital pins
  lcd.begin(16,2);
  Serial.begin(19200);
  Wire.begin();
  vaisala.begin(19200);
  lcd.print(" Vaisala WXT520 ");
  lcd.setCursor(0,1);
  lcd.print(" data logger v1 ");
  delay(3000);
  if (! RTC.isrunning()) {
    lcd.clear();
    lcd.print("RTC not OK");
    Serial.println("RTC is NOT running!");
    //RTC.adjust(DateTime(__DATE__, __TIME__));
  }
  delay(100);
  Serial.print("Initializing SD card...");
  lcd.clear();
  lcd.print("SD card init...");
  delay(1000); // to be able to see the message
 
  pinMode(10, OUTPUT);
 
  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    lcd.clear();
    lcd.print("SD error !!!");
    Serial.println("Card failed, or not present");
    return;
  }
  Serial.println("card initialized.");
  lcd.clear();
  lcd.print("SD card OK!");
  delay(1000); // to be able to see the message
  lcd.clear();
  lcd.print(" Receiving data ");
 
}
 
void loop()
{
  vaisala.println(command); //println because CR+LF must be sent
  // Read all serial data available, as fast as possible
   
  while(vaisala.available() > 0) // this should become FALSE when there is no more new bytes - it will be -1
  {
    delay(200); // let the buffer fill up
    char inChar = vaisala.read(); // every byte from buffer will be compared to EOD
    if(inChar == EOD) // if EOD is detected, set 'ended' to TRUE and get out of while loop (jump to packet processing section)
    {
       ended = true;
       break; // exit WHILE loop
    }
    else
    {
    if(index < 127) // EOD not detected so loop further through indexes
      {
        inData[index] = inChar;
        index++;
        // if following line is uncomented, I get one empty line after each line of data !!!
        //inData[index] = '\0'; // terminate with a NULL so that char array can qualify as string
      }
    }
  }
 
  if(ended) //EOD marker detected, process data
  {
      DateTime now = RTC.now();                              // get time from RTC 
      getFilename(filename); // call method that constructs the file name using "date" data
      File dataFile = SD.open(filename, FILE_WRITE);
 
        // if the file is available, write to it:
        if (dataFile)
        {
          // time stamp from RTC
          dataFile.print(now.day(), DEC);
          dataFile.print('/');
          dataFile.print(now.month(), DEC);
          dataFile.print('/');
          dataFile.print(now.year(), DEC);
          dataFile.print(" , ");
          dataFile.print(now.hour(), DEC);
          dataFile.print(':');
          dataFile.print(now.minute(), DEC);
          dataFile.print(" , ");
          dataFile.println(inData);
          dataFile.close();
          Serial.println(inData); // attach received data
        }
         // if the file isn't open, pop up an error:
        else
        {
          Serial.print("error opening ");
          Serial.println(filename);
        }
     // Reset for the next packet
    ended = false;
    index = 0;
    //inData[index] = '\0';
  }
     delay(5000); // how often to query for new data from serial device
}

// method for constructing the file name
void getFilename(char *filename) {
  DateTime now = RTC.now();
  int year = now.year();
  int month = now.month();
  int day = now.day();
  filename[0] = '2';
  filename[1] = '0';
  filename[2] = (year-2000)/10 + '0';
  filename[3] = year%10 + '0';
  filename[4] = month/10 + '0';
  filename[5] = month%10 + '0';
  filename[6] = day/10 + '0';
  filename[7] = day%10 + '0';
  filename[8] = '.';
  filename[9] = 'T';
  filename[10] = 'X';
  filename[11] = 'T';
  return;
}

Either I'm not detecting end of line marker (which is CR+LF), or there is some other error.
LCD is showing "Receiving data" message, but in Serial monitor I can't see received data, and SD card is empty (the file havent been created at all because no data is processed).

Thanks in advance!
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 601
Posts: 48543
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Code:
    delay(200); // let the buffer fill up
This is silly. You know what marks the end of a message. Read and store data, as fast as possible as the data arrives. It will not all arrive in one pass through loop(), no matter how little data there is, and how fast the serial baud rate is, unless you use a very large delay.

Code:
  vaisala.println(command); //println because CR+LF must be sent
Sending the command on EVERY pass through loop() doesn't make sense. How often do you really need to do this?

Code:
        // if following line is uncomented, I get one empty line after each line of data !!!
Because the device is sending a carriage return and a line feed. The "empty line" is the line feed. You need to ignore both CR and LF, NOT skip the needed NULL termination.

Code:
    //inData[index] = '\0';
This needs to be uncommented.

Code:
  filename[0] = '2';
  filename[1] = '0';
  filename[2] = (year-2000)/10 + '0';
  filename[3] = year%10 + '0';
  filename[4] = month/10 + '0';
  filename[5] = month%10 + '0';
  filename[6] = day/10 + '0';
  filename[7] = day%10 + '0';
  filename[8] = '.';
  filename[9] = 'T';
  filename[10] = 'X';
  filename[11] = 'T';
Or use sprintf()...
Logged

Serbia
Offline Offline
Newbie
*
Karma: 0
Posts: 13
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

1. Baud rate is "19200" and I know that END marker should be CR+LF. I've used '\n' but I don't think it's detected properly. My code is working when I'm using another PC, if I type a couple of characters and hit 'enter', Arduino receives it OK and all looks well (even if I comment '\0' line). So, I'm thinking that Vaisala device "can't hit 'enter' " or I don't know how to capture it, or how to mach the end of data.
When I did some testing using PC2 and opened a file from SD card (using Notepad++) I can see that every line that was received by Arduino is ended with CR+LF, so '\n' should be OK for 'end of data marker', but I can't figure out why it's not working when Vaisala is attached  smiley-sad

2. I need to send "0XR" command every time when I want new data from Vaisala. So, whenever I read one set of data, I need to send new request (pool the data) in order to receive new set of data. My idea is to set the delay at the end of loop(). If I want to query for data every 10 seconds, delay would be (10000). If there is some other solution I'll be glad to try it.
3. about sprintf() - will take a look and try to understand why it is better (this part in my code, and almost everything else, is copied from existing examples).
I'll do my best to make some progress, thanks a lot for all the comments and suggestions PaulS !!!
Logged

Serbia
Offline Offline
Newbie
*
Karma: 0
Posts: 13
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I've managed to receive the data from sensor, but only by using 'for' loop. I did the same for reading and printing the buffer. This only works if length of data is smaller than 63 bytes, if length is larger (in my case, each received message is between 150 and 170 bytes long, depending on measured values), everything falls apart smiley-sad
I can customize the content of message in order to make it shorter, but it would be shame to lose all that info coming from device.
I've read of possibility to increase the buffer, but that is beyond my capabilities.
The biggest problem is how to detect 'end of message', I simply can't catch that info!
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 601
Posts: 48543
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
2. I need to send "0XR" command every time when I want new data from Vaisala. So, whenever I read one set of data, I need to send new request (pool the data) in order to receive new set of data. My idea is to set the delay at the end of loop().
My idea is that you look at, understand, and embrace the idea of the blink without delay example.

Echo each character read from the device to the Serial Monitor, as both a character and as a HEX value. I think pretty quickly, you'll get a handle on the output.

And forget about any use of delay(). Use millis() and a stored time (last time you sent data) to determine if it is time to send again.
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 601
Posts: 48543
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
This only works if length of data is smaller than 63 bytes, if length is larger (in my case, each received message is between 150 and 170 bytes long, depending on measured values), everything falls apart
You can read serial data orders of magnitude faster than it can be received. If you are having troubles storing the data as fast as it can be read, you are storing it wrong.
Logged

Serbia
Offline Offline
Newbie
*
Karma: 0
Posts: 13
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I think I've understood your point regarding delay(), and will try to switch to millis().
If serial buffer is based on FIFO, I don't see any problem about incoming data length, as long as there is incoming data my 'while available()>0' condition should be TRUE, and my 'inData char array' should be populated properly. However, the data is coming very fast and if I print the value of 'available()' in Serial monitor I can see that it is always begins with 63 and counts down to 0, so the buffer is read OK and sketch is working good if data is less than 63 bytes. If data is more than 63 bytes, I can't understand what is happening smiley-sad

Forgive my lack of knowledge, but I'm eager to get this into my head and try to learn what is going on  smiley-red
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 601
Posts: 48543
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
However, the data is coming very fast
But not faster than it is possible for the Arduino to read it.

Quote
However, the data is coming very fast and if I print the value of 'available()' in Serial monitor I can see that it is always begins with 63 and counts down to 0
Then, you are diddling around somewhere, before getting back to reading data. It is impossible for 63 or more characters to arrive in one iteration of loop(), unless something is causing a delay().

Quote
If data is more than 63 bytes, I can't understand what is happening
Would you like us to guess, or would you rather post your code?
Logged

Pages: [1] 2   Go Up
Jump to: