Struct

Hi,

I am having a weeny trouble with my dust sensor. I have a long hex data output (I am getting all this numbers in one message). Can you help me with cutting this into a smaller pieces by function struct? Or is there some better function for this?

42
4D
0
1C
0
1F
0
22
0
25
0
21
0
24
0
27
0
0
0
0
0
0
0
0
0
0
0
0
C
0
1
89

Yeah, but you have to give use some more info. Start by telling us which sensor exactly. Also post the code you use at the moment (use code tags!) and what exactly you want to do with it in the end (the real application, not something about structs).

Ok,

SM-UART-04L, I want to have separated data.

sm-uart.ino (329 Bytes)

That's a weird goal... I would think you wanted at least to display the values or something ::slight_smile:

Aka, typical XY-problem!

If you want each piece of data to be accessible individually then the obvious thing to do is to put them into an array as they are read. How many values do you want/need to read in before using them ?

How and when will the values be used ? More details please

I want to use all data - they are now in hex and i want them in dec, so i should to convert them and for this i need them separated.

How many is "all" ?

There is no need to convert them. It is your use of the HEX parameter when printing them that shows them in Hex format. Remove it

And I know, that I can replace the word HEX with word DEC, but you need to know some letters in the end of the code, because they are telling you what went wrong.

sm-uart.ino (329 Bytes)

Decimal and hex are just representation. In the end it's just stored binary. .print() takes care of making it text so we humans can read it more easily.

But I would just assign it to the correct variable when it comes in. Just count and assign.

Trouble is, the datasheet of that thing is terrible. If the device just outputs data randomly, does it have a significant pause between bursts? Or is the header and CRC the only thing you might use to sync?

PS Note the serial on the device is 3,3V So using a 5V Arduino to send data (or even connect it to the Rx of the sensor) can damage it!

septillion:
Trouble is, the datasheet of that thing is terrible. If the device just outputs data randomly, does it have a significant pause between bursts? Or is the header and CRC the only thing you might use to sync?

I found this datasheet (attached), page 4 shows there is a 2 byte header and a 2 byte check sum at the end and next page shows the "Response Mode - Command Frame"

I would suggest to study Serial Input Basics to handle this

AAS-916-139B-Telaire-SM-UART-04L_EN_021819-web.pdf (1.15 MB)

1 Like

Do you want to see your data displayed in the following format? If yes, upload the sketch given below. This sketch looks for the header (0x424D) and then accepts the remaining incoming data bytes.
smSmoke.png

Sketch

#include<SoftwareSerial.h>
SoftwareSerial SUART(4, 5); //SRX=DPin-4, STX=DPin-5
bool flag1 = false;
bool flag2 = false;
byte rxData[30];

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

}

void loop()
{
  byte n = SUART.available();
  {
    if (n != 0)
    {
      if (flag2 == false)
      {
        if (flag1 == false)
        {
          byte x = SUART.read();
          if (x == 0x42)
          {
            flag1 = true;
          }
        }
        else
        {
          byte x = SUART.read();
          if (x == 0x4D)
          {
            flag2 = true;
          }
        }
      }
      else
      {
        byte m = SUART.readBytesUntil('\n', rxData, 30);
        Serial.print("Standard Smoke CV1: "); Serial.println(rxData[2]<<8|rxData[3]);
        Serial.print("Standard Smoke CV2: "); Serial.println(rxData[4]<<8|rxData[5]);
        Serial.print("Standard Smoke CV3: "); Serial.println(rxData[6]<<8|rxData[7]);
        Serial.println();
        Serial.print("Enviorment CV1: "); Serial.println(rxData[8]<<8|rxData[9]);
        Serial.print("Enviorment CV2: "); Serial.println(rxData[10]<<8|rxData[11]);
        Serial.print("Enviorment CV3: "); Serial.println(rxData[12]<<8|rxData[13]);
           
       // for (int i = 0; i < m; i++)
       // {
      //    Serial.print(rxData[i], HEX);
      //    Serial.print(' ');
       // }
        memset(rxData, 0x00, 30);
        flag1 = false;
        flag2 = false;
        Serial.println();
        Serial.println("==================================");
      }
    }
  }
}

Dust Sensor Simulation Codes using NANO

#include<SoftwareSerial.h>
SoftwareSerial SUART(4, 5); //SRX=DPin-4, STX=DPin-5
byte txData[] = 
{
  0x42, 0x4D, 0x00, 0x1C, 0x00, 0x1F, 0x00, 0x22, 
  0x00, 0x25, 0x00, 0x21, 0x00, 0x24, 0x00, 0x27, 
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
  0x00, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x01, 0x89
};

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

void loop() 
{
  SUART.write(txData, sizeof(txData));
  delay(1000);

}

smSmoke.png

Wow, that's a lot of mess to simply store some values! :o And still can be out of sync :confused:

So, you have data coming in as hexadecimal numbers in ASCII, separated by line feeds??
Or are you getting bytes and showing us the hex equivalent?

When you say function struct, do you mean that this is structured data? That you get 32 bytes and you'd like those 32 bytes stuck into a struct? That's do-able. Use a union. Kinda like this:

union {
  byte as_array[32];
  struct Foo {
    int a, b;
    int some_more_values[5];
    char a_String[10];
    // etc
  } as_struct;
} input;

for(i= 0; i < 32; i++) {
  input.as_array[i] = read_a_byte();
}

Serial.print("the third value is ");
Serial.println(input.as_struct.some_more_values[2]);

Beware of endian-ness.

septillion:
Wow, that's a lot of mess to simply store some values! :o And still can be out of sync :confused:

Looking at the spec, you need to await for 0x424D, then start listening all the way to the check sum. if the check sum does not match then trace back to see if 0x424D was in the data in between and assume this is the start, wait for a bit more until new check sum etc.. At some point you'll get in sync.

J-M-L:
Looking at the spec, you need to await for 0x424D, then start listening all the way to the check sum. if the check sum does not match then trace back to see if 0x424D was in the data in between and assume this is the start, wait for a bit more until new check sum etc.. At some point you'll get in sync.

In my sketch of Post#10, I have exactly done what has been stated above except that I have not calculated the run-time CHKSUM which could be easily implemented.

I believe that detection of the header should be enough for synchronization as the number of data bytes are known. The user can calculate the CHKSUM on the received bytes and then compare it with the received CHKSUM to ensure the quality of the data.

GolamMostafa:
I believe that detection of the header should be enough for synchronization as the number of data bytes are known. The user can calculate the CHKSUM on the received bytes and then compare it with the received CHKSUM to ensure the quality of the data.

if you are unlucky and 0x424D is a possible value within a frame, then at the start of the system you end up out of sync and mess up a couple reads. That's why checking the CS helps increase likelihood of being aligned

J-M-L:
if you are unlucky and 0x424D is a possible value within a frame, then at the start of the system you end up out of sync and mess up a couple reads. That's why checking the CS helps increase likelihood of being aligned

You are right. In case, there are data bytes like 0x42 and 0x4D in the transmission frame then the mess is there as they might be taken as header. (+).

This is the sketch that checks both the header (0x424D) and the CHKSUM for match and then accepts the frame as containing valid data.

#include<SoftwareSerial.h>
SoftwareSerial SUART(4, 5); //SRX=DPin-4, STX=DPin-5
bool flag1 = false;
bool flag2 = false;
byte rxData[30];
byte y1, y2;

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

}

void loop()
{
  byte n = SUART.available();
  {
    if (n != 0)
    {
      if (flag2 == false)
      {
        if (flag1 == false)
        {
          byte x = SUART.read();
          y1 = x;
          if (x == 0x42)
          {
            flag1 = true;
          }
        }
        else
        {
          byte x = SUART.read();
          y2 = x;
          if (x == 0x4D)
          {
            flag2 = true;
          }
        }
      }
      else
      {
        byte m = SUART.readBytes(rxData, 30);  //Edit ; SUART.readBytesUntil('\n', rxData, 30);
        //Serial.println(m);
        int CHKSUM = 0;
        for (byte i = 0; i < m - 2; i++)
        {
          CHKSUM += rxData[i];
        }
        CHKSUM = CHKSUM + y1 + y2;
        //Serial.println(CHKSUM, HEX);
        if (CHKSUM == (rxData[28] << 8 | rxData[29]))
        {
          Serial.println("Received data are valid.");
          Serial.print("Standard Smoke CV1: "); Serial.println(rxData[2] << 8 | rxData[3]);
          Serial.print("Standard Smoke CV2: "); Serial.println(rxData[4] << 8 | rxData[5]);
          Serial.print("Standard Smoke CV3: "); Serial.println(rxData[6] << 8 | rxData[7]);
          Serial.println();
          Serial.print("Enviorment CV1: "); Serial.println(rxData[8] << 8 | rxData[9]);
          Serial.print("Enviorment CV2: "); Serial.println(rxData[10] << 8 | rxData[11]);
          Serial.print("Enviorment CV3: "); Serial.println(rxData[12] << 8 | rxData[13]);

          // for (int i = 0; i < m; i++)
          // {
          //    Serial.print(rxData[i], HEX);
          //    Serial.print(' ');
          // }
          memset(rxData, 0x00, 30);
          flag1 = false;
          flag2 = false;
          Serial.println();
          Serial.println("==================================");
        }
        else
        {
          Serial.println("Invalid Data...!");
          memset(rxData, 0x00, 30);
          flag1 = false;
          flag2 = false;
        }
      }
    }
  }
}

GolamMostafa:
CHKSUM can not be used to synchronize the telemetry frame as the value of CHKSUM will change with data.

Yes you can, simply calculate together with the sensor :wink:

GolamMostafa:
The only way, in this case, to sync the frame is to detect the known header (0x424D) and then read the remaining 30 bytes data.

But it's not flawless. The data may also contain 0x424D bringing you out of sync.

So I would hope the sensor sends out data in bursts so you can simply time out and know the next byte is the start (and extra check it to be 0x424D).

[edit] ow, didn't notice page 2...

GolamMostafa:
You are right. In case, there are data bytes like 0x42 and 0x4D in the transmission frame then the mess is there as they might be taken as header. (+).

that's indeed one of the value of CRC of CS - ensure you lined things up correctly

septillion:
Yes you can, simply calculate together with the sensor :wink:
But it's not flawless. The data may also contain 0x424D bringing you out of sync.

Not really as you don't look anymore for a start frame signal when receiving the data

The way it works:
You read and wait for 0x424D. Once you have identified an incoming 0x424D you mark that as start of the frame and you keep a running buffer of what comes in, you don't check for 0x424D anymore, you read the expected number of bytes.

Once all the bytes are in, you verify the check sum.

  • If it is right, then your assumption of the start of the frame is (most likely) right.

  • If it is wrong then either the frame was corrupted or you started receiving in the middle of a frame and the 0x424D was actual data. At that point you look into all the data you've received if you have 0x424D. If so you assume this is the new start, shift your data in the running buffer (ideally circular) and wait again for a complete frame, check again the CS etc...

At some point you'll get a match as you'll be lined up on the real frame. If you fail fore more than the length of 2 frames, then it means the data you get in might be corrupted. (you might want to wait for 10 corrupted frames in a row to report error for example).