[SOLVED] Where do I begin? reading data from Weather Station and display it

I really don't know were to start...

I have a Peet Bros Ultimeter 2100 weather station and for > 20 years now, I enjoy reading its data on what they call a "Weather Picture" in my living room:

But now I discovered the Arduino, it is possible to make additional displays and make much more data visible!
The weather station has a serial port from which it continuously send out very detailed weather data, WONDERFUL!!

I don't ask for a complete solution but can anyone please point me to the right direction?

My goal it so select maybe 10-12 separate 4 byte fields from the complete string of data, convert it to decimal and display it on a large number of 7 segment displays. :slight_smile:

So what is the best approach to read and select specific portions of this data coming in??
The weather data fields length and position is fixed so that's good.
But one 'burst' of data if too large to fit in one array??
I did some experiments but I got stuck, also by reading a lot about serial data.

I am lost... :astonished:

This is one complete line of data coming in:

  • header (&CR&)
  • 452 HEX digits
  • CR and LF
    (the dashes mean no data available I think)

&CR&007000CC00F400BB029402940000273E000186780001034D------------00E004BE023901680241017200E002390168023901680241017200E002390168
273A0326007000CC275304CB00DD26F1042A02E201B402EA01B700E002E201B
403F2000003F2000000DC0000034603F2000003F2000000DC00000346013
C041E01CD033100DD01CE050402DD0380006F00D602CC038700DC02FF03BE2
75C00002774026500DC2823034D034E04AC033904A700DC0356034F000000
000000000000DC00000346000000000000000000DC00000346000200D
D001600020000BEB8B5000071

for example (above data string):
The first 4 bytes = 1. Wind Speed (0.1kph) = 0070h = 112 DEC = 11,2 km/h
The 2nd 4 bytes = 2. Current Wind Direction (0-255) = 00CCh = 204 degrees
etc.

RECORD STRUCTURE

Header = &CR&, Data Fields (note: field nos. 111-114 are 2-digits; all others are 4-digits)

  1. Wind Speed (0.1kph)
  2. Current Wind Direction (0-255)
  3. 5 minute Wind Speed Peak (0.1 kph)
  4. 5 minute Wind Direction Peak (0-255)
  5. Wind Chill (0.1 deg F)
  6. Outdoor Temp (0.1deg F) (’97 and later)
  7. Rain Total for today (0.01 in.)
  8. Barometer (0.1 mbar)
  9. Barometer 3-Hour Pressure Change (0.1 mbar)
  10. Barometer Correction Factor(LSW)
  11. Barometer Correction Factor(MSW)
  12. Indoor Temp (0.1 deg F)
  13. Outdoor Humidity (.1%)
  14. Indoor Humidity (.1%)
  15. Dew Point (0.1 deg F)
  16. Date (day of year)
  17. Time (minute of day)
  18. Today's Low Chill Value
  19. Today's Low Chill Time
  20. Yesterday's Low Chill Value
  21. Yesterday's Low Chill Time
  22. Long Term Low Chill Date
  23. Long Term Low Chill Value
  24. Long Term Low Chill Time
  25. Today's Low Outdoor Temp Value
  26. Today's Low Outdoor Temp Time
  27. Yesterday's Low Outdoor Temp Value
  28. Yesterday's Low Outdoor Temp Time
  29. Long Term Low Outdoor Temp Date
  30. Long Term Low Outdoor Temp Value
  31. Long Term Low Outdoor Temp Time
  32. Today's Low Barometer Value
  33. Today's Low Barometer Time
  34. Wind Speed (0.1kph)
  35. Current Wind Direction (0-255)
  36. Yesterday's Low Barometer Value
  37. Yesterday's Low Barometer Time
  38. Long Term Low Barometer Date
  39. Long Term Low Barometer Value
  40. Long Term Low Barometer Time
  41. Today's Low Indoor Temp Value
  42. Today's Low Indoor Temp Time
  43. Yesterday's Low Indoor Temp Value
  44. Yesterday's Low Indoor Temp Time
  45. Long Term Low Indoor Temp Date
  46. Long Term Low Indoor Temp Value
  47. Long Term Low Indoor Temp Time
  48. Today's Low Outdoor Humidity Value
  49. Today's Low Outdoor Humidity Time
  50. Yesterday's Low Outdoor Humidity Value
  51. Yesterday's Low Outdoor Humidity Time
  52. Long Term Low Outdoor Humidity Date
  53. Long Term Low Outdoor Humidity Value
  54. Long Term Low Outdoor Humidity Time
  55. Today's Low Indoor Humidity Value
  56. Today's Low Indoor Humidity Time
  57. Yesterday's Low Indoor Humidity Value
  58. Yesterday's Low Indoor Humidity Time
  59. Long Term Low Indoor Humidity Date
  60. Long Term Low Indoor Humidity Value
  61. Long Term Low Indoor Humidity Time
  62. Today's Wind Speed Value
  63. Today's Wind Speed Time
  64. Yesterday's Wind Speed Value
  65. Yesterday's Wind Speed Time
  66. Long Term Wind Speed Date
  67. Long Term Wind Speed Value
  68. Long Term Wind Speed Time
  69. Today's High Outdoor Temp Value
  70. Today's High Outdoor Temp Time
  71. Wind Speed (0.1kph)
  72. Current Wind Direction (0-255)
  73. Yesterday's High Outdoor Temp Value
  74. Yesterday's High Outdoor Temp Time
  75. Long Term High Outdoor Temp Date
  76. Long Term High Outdoor Temp Value
  77. Long Term High Outdoor Temp Time
  78. Today's High Barometer Value
  79. Today's High Barometer Time
  80. Yesterday's High Barometer Value
  81. Yesterday's High Barometer Time
  82. Long Term High Barometer Date
  83. Long Term High Barometer Value
  84. Long Term High Barometer Time
  85. Today's High Indoor Temp Value
  86. Today's High Indoor Temp Time
  87. Yesterday's High Indoor Temp Value
  88. Yesterday's High Indoor Temp Time
  89. Long Term High Indoor Temp Date
  90. Long Term High Indoor Temp Value
  91. Long Term High Indoor Temp Time
  92. Today's High Outdoor Humidity Value
  93. Today's High Outdoor Humidity Time
  94. Yesterday's High Outdoor Humidity Value
  95. Yesterday's High Outdoor Humidity Time
  96. Long Term High Outdoor Humidity Date
  97. Long Term High Outdoor Humidity Value
  98. Long Term High Outdoor Humidity Time
  99. Today's High Indoor Humidity Value
  100. Today's High Indoor Humidity Time
  101. Yesterday's High Indoor Humidity Value
  102. Yesterday's High Indoor Humidity Time
  103. Long Term High Indoor Humidity Date
  104. Long Term High Indoor Humidity Value
  105. Long Term High Indoor Humidity Time
  106. Yesterday's Rain Total (0.01")
  107. Long Term Rain Date
  108. Long Term Rain Total (0.01")
  109. Leap Year Value (0-3)
  110. WDCF Value (0-255)
  111. Today's High Wind Direction (2 digits)
  112. Yesterday's High Wind Direction (2 digits)
  113. Spare (2 digits)
  114. Long Term High Wind Direction (2 digits)
  115. 1 Minute Wind Speed Average (0.1kph)
    Carriage Return & Line Feed

?Total size: 452 characters (hex digits) plus header, carriage return and line feed.

I'm not sure if your incoming data is in Hex or raw data that you converted to hex for display

Anyway, you can do it by creating a struct with all your data fields in it (I just called them num1 num2 etc in my example but you can give them the real names and you also need to specify whether they are int's (16 bit values) or bytes (8 bit values)

Then define a pointer to the start of the struct and read the data straight into the struct

However. a big issue is that the data is the opposite endian ness to the Arduino, so you'd need to byte swap every time you wanted to use the data.

But you can overload multiple byteswap functions for the different data types, and the compiler will automatically handle it for you.

Note, in this example I'm writing the data into the struct as if the incoming data was 000102030405060708

long byteSwap(long num)
{
  return ((num>>24)&0xff) | // move byte 3 to byte 0
                    ((num<<8)&0xff0000) | // move byte 1 to byte 2
                    ((num>>8)&0xff00) | // move byte 2 to byte 1
                    ((num<<24)&0xff000000); // byte 0 to byte 3
  
}

int byteSwap(int num)
{
  return (num>>8) | (num<<8);
}

 byte byteSwap( byte num)
{
  return num;
}

 struct 
 {
   int num1;
   int num2;
   int num3;
   int num4;
   byte num5;
 } myData;



void setup()
{ 
 //Initialize serial and wait for port to open:
  Serial.begin(115200); 
unsigned char *conv = (unsigned char *)&myData;
for(int i=0;i<sizeof(myData);i++)
{
*conv++=i;
}


Serial.println(byteSwap(myData.num1));
Serial.println(byteSwap(myData.num2) );
Serial.println(byteSwap(myData.num3) );
Serial.println(byteSwap(myData.num4) );
Serial.println(byteSwap(myData.num5) );
} 

void loop()
{
  
}

Thanks Roger for taking the time to answer!!

The data I'm getting in is HEX, but that's not important I think?

As a newbie I have to dive into the concept of struct and pointers so I will read your sketch very carefully and maybe come back if I'm stuck.. :wink:

No worries.

You can convert hex characters to real numbers fairly easily and then store in the struct, but you will need to write code to wait for the start pattern, etc. so you will probably need to write a finite state machine as well.

But take one step at a time

Given the size of the data stream and the general paucity of RAM on most arduinos, I'd be inclined to to try parsing it field by field as you receive it. Serial data transmission is slow compared to the Arduino clock speed so you should have plenty of time. Given the age of your weather station, I'd expect the baud rate to be pretty low too.

As you read the serial stream, keep note of which field you're on so you know how big it is and whether you want it. If you do, copy it to a little buffer and call a routine to process it, which will likely consist of a switch statement controlled by field number.

Of course, if your sketch is simple enough, you may do just as well buffering a complete packet but the above method may help when you start adding features and use up your remaining RAM.

@RogerClarck: thank you!!

@wildbill: I fiddled somewhat with the incoming data and I realized that it's too complicated (for me) to capture all data and display it. And yes, the weather station is sending it's data at 2400 baud.

The original "Weather Picture" has a dedicated processor for 1 display. So if you want to display the temperature, you select on a DIP switch the right parameters and only the temp will be shown. All displays are connected in parallel.

But more important: I wondered why the WIND direction and speed was updated not every 2 seconds but 3 times in that time period. The solution is simple: the value of those parameters is put in the data stream 3 times! (positions 1, 35, 71)
That means I have to update the display WHILE reading the data stream and not AFTER it.

But that makes the reading much less complicated I think?

display board

Dip Switch to select value to be displayed

display board dedicated to wind direction
MC68HC705C8A microcontroller

DIP SWITCH is used to select ONE parameter/display

Linked together in parallel

For others struggling to cope with the serial data coming in, this is a MUST READ:

@RogerClark: your suggestion of using a 'state 'machine' is spot on, that is what I need... I will dive into it and try to get it to work. Thanks!

Beware: as you can see I'm not a trained programmer...

Why does this not work...
The program does not catch the start of a line, let alone handle reading the desired bytes and displaying it...

Data is coming in into the serial port CONSTANTLY!!
452 bytes = 1 complete 'message' at 2400 baud

The 'simple' goal is:

  1. to search for the start of a new message
  2. at 3 places in the message I want to capture 4 consecutive bytes
  3. combine the 4 bytes to get a HEX number
  4. convert it to decimal
  5. display it on a 7 segment display
  6. start at line 1

a 'Final State Machine' does not work, I don't understand how to implement this in my case.

help is very much appreciated!

//----------------------------------------------------------------------------------------------------------
// Libraries
//----------------------------------------------------------------------------------------------------------
#include <SoftwareSerial.h>
#include <Streaming.h>
#include "LedControl.h"

//----------------------------------------------------------------------------------------------------------
// Maxim 7219 Matrix Display initialization
//----------------------------------------------------------------------------------------------------------
/*
  clearDisplay(int addr) ........................................ clears the selected display
  lc.shutdown(int addr, boolean) ................................ wake up the MAX72XX from power-saving mode (true = sleep, false = awake)
  lc.setIntensity(int addr, value) .............................. set a medium brightness for the Leds (0=min - 15=max)
  lc.setLed(int addr, int row, int col, boolean state) .......... switch on the led in row, column. remember that indices start at 0!
  lc.setRow(int addr, int row, byte value) ...................... this function takes 3 arguments. example: lc.setRow(0,2,B10110000);
  lc.setColumn(int addr, int col, byte value) ................... this function takes 3 arguments. example: lc.setColumn(0,5,B00001111);
  lc.setDigit(int addr, int digit, byte value, boolean dp) ....... this function takes an argument of type byte and prints the corresponding digit on the specified column.
                                                                  The range of valid values runs from 0..15. All values between 0..9 are printed as digits,
                                                                  values between 10..15 are printed as their hexadecimal equivalent
  lc.setChar(int addr, int digit, char value, boolean dp) ....... will display: 0 1 2 3 4 5 6 7 8 9 A B C D E F H L P; - . , _ <SPACE> (the blank or space char)

pin 12 is connected to the DataIn
pin 11 is connected to the CLK
pin 10 is connected to CS/LOAD

***** Please set the number of devices you have *****
But the maximum default of 8 MAX72XX wil also work.
LedConrol(DATAIN, CLOCK, CS/LOAD, NUMBER OF MAXIM CHIPS)
*/
LedControl lc = LedControl(12, 11, 10, 2);


//----------------------------------------------------------------------------------------------------------
// SoftSerial port
//----------------------------------------------------------------------------------------------------------
SoftwareSerial ultimeterPort(2, 3); // RX, TX

//----------------------------------------------------------------------------------------------------------
// definitions
//----------------------------------------------------------------------------------------------------------

int positionCounter = 0; // counter to determine at which position data is read
char value = 0;
boolean validData = 0; // used to check if all 4 bytes are available for display

//==============================================================================
// SETUP
//==============================================================================

void setup ()
{
  Serial.begin (115200);
  pinMode(13, OUTPUT);

  // SoftSerail - Peet Bros Ultimeter 2100 serial output is 2400 baud
  ultimeterPort.begin(2400);

  // The MAX72XX is in power-saving mode on startup,
  // we have to do a wakeup call
  for (int displaynumber = 0; displaynumber < 2; displaynumber++)
  {
    lc.shutdown(displaynumber, false);
    // Set the brightness to a medium values
    lc.setIntensity(displaynumber, 8);
    // and clear the display
    lc.clearDisplay(displaynumber);
  }

}  // end of setup


//==============================================================================
// Loop()
//==============================================================================
void loop ()
{
  if (ultimeterPort.available ())
    processIncomingByte (ultimeterPort.read());


  if (validData == 1)
  {
    lc.setDigit(0, 0, positionCounter / 100, false);
    lc.setDigit(0, 1, positionCounter / 100, false);
  }


}



//==============================================================================
// processIncomingByte
//==============================================================================
void processIncomingByte (const char c)
{
  if (c == '\r') // look for end (CR) of the Serial message
  {
    positionCounter = 0; // reset position counter
    digitalWrite(13, HIGH);
    value = ' ';
    validData = 0;
  }
  else
  {
    digitalWrite(13, LOW);
    positionCounter++; // beginning of Serial message detected, look for position of desired value

    switch (positionCounter)
    {
      case '7': // get bit on position 7
        value += c;
        break;
      case '8': // get bit on position 8
        value += c;
        break;
      case '9': // get bit on position 9
        value += c;
        break;
      case '10': // get bit on position 10
        value += c;
        validData = 1; // four bytes filled in so ready to display
    }  // end of switch on incoming byte
  } // else

} // void processIncomingByte

I changed the input from the serial stream to the 'real' input, pin 2 instead of using SoftSerial and now things start working as expected!

I will never use the Software serial again... :frowning:

SOLVED
I found my own way/solution but a big thank you to all who took the trouble to give me hints.

It's working, now I can go on and design a PCB and make a "Weather display"

Video: https://flic.kr/p/oujseT

Can you share the code? i have a similar issue