binary serial communication timing

Hi,
Bear with me as this might be a long post to gather what I'm trying to do.
I've been sniffing a serial communication between two atmel chips, so I've determined they're at 9600baud and have managed to identify they are sending 8-byte binary data with defined start and end bytes so from that I can determine the total data frame of 10 'blocks'.

So when I run a simple serial monitor code I see the data simply outputting the raw data line this. Here is my simple code (Running on ESP8266 lolin)

#include <SoftwareSerial.h> // This is the one for ESP8266 specifically
SoftwareSerial swSer1(11, 12, false); // RX port then TX port
#define MAX_MILLIS_TO_WAIT 8000  //How long to wait for serial data to avoid hanging code
byte startByte = 188;                     // <- start byte of serial 10111100
byte endByte = 253;                       // <- stop byte of serial 11111101
boolean newData = false;
byte ch;
byte ser_indata[10]; // byte array for serial binary data

void setup() {
  Serial.begin(115200);
  delay(3000);
  Serial.println(F(" Debug serial binary output"));
  swSer1.begin(9600,SWSERIAL_8N1);
}

void loop() {
  static boolean recvInProgress = false;
      static byte ndx = 0;
      uint8_t rc;
    unsigned long starttime = millis();
    while (swSer1.available() > 0 && newData == false && ((millis() - starttime) < MAX_MILLIS_TO_WAIT)) {      
      rc = swSer1.read();
      Serial.println(rc,BIN);
      if (recvInProgress == true) {
        if (rc != endByte) {
          ser_indata[ndx] = rc;
          ndx++;
        } else {
          end_byte=runtime;
          recvInProgress = false;
          ndx = 0;
          newData = true;
          swSer1.flush(); // flush now we have a valid pack of data?
        }
      } else if (rc == startByte) {
        start_byte=runtime;
        recvInProgress = true;
      };
    };
    if (newData == true) {
      Serial.println("Data found:");
      for(int i = 0; i < sizeof(ser_indata); i++)
      {
          Serial.print(ser_indata[i]);
          Serial.print("  =  ");
         Serial.println(ser_indata[i],BIN);
      };
      newData = false;
    };
}

So this looks like its working. Now what I want to do is 'record' a set of data (it just repeats the code constantly) and play it back, but timing is then important so I need to record the timing used on serial too and play it back at the right speed/timing, this is starting to give me a headache!
Am I right in thinking I record the millis at startByte being found then when I get to endByte I can record millis, take one from the other and I then know the timing or is that just the send timing, not each of the block timing?

My output from above comes back with:

10111011
11
0
0
0
11
11111101
Data found:
3  =  11
0  =  0
0  =  0
0  =  0
3  =  11
0  =  0
0  =  0
0  =  0
0  =  0
0  =  0

And it repeats.
Am I on the right track and is it possible to buffer this, record the timing and then send it in a separate for loop later?

Thanks in advance!
Andy

andyb2000:
so I need to record the timing used on serial too and play it back at the right speed/timing,

I don't understand what you have in mind by "timing". For serial communication the baud rate determines the timing.

...R

You can use SerialTransfer.h to automatically packetize and parse your data for inter-Arduino communication without the headace. The library is installable through the Arduino IDE and includes many examples.

Here are the library's features:

This library:

  • can be downloaded via the Arduino IDE's Libraries Manager (search "SerialTransfer.h")
  • works with "software-serial" libraries
  • is non blocking
  • uses packet delimiters
  • uses consistent overhead byte stuffing
  • uses CRC-8 (Polynomial 0x9B with lookup table)
  • allows the use of dynamically sized packets (packets can have payload lengths anywhere from 1 to 254 bytes)
  • can transfer bytes, ints, floats, and even structs!!

Example TX Arduino Sketch:

#include "SerialTransfer.h"

SerialTransfer myTransfer;

void setup()
{
  Serial.begin(115200);
  Serial1.begin(115200);
  myTransfer.begin(Serial1);
}

void loop()
{
  char buff[] = "hi";

  myTransfer.txObj(buff, sizeof(buff));
  myTransfer.sendData(sizeof(buff));
  delay(100);
}

Example RX Arduino Sketch:

#include "SerialTransfer.h"

SerialTransfer myTransfer;

void setup()
{
  Serial.begin(115200);
  Serial1.begin(115200);
  myTransfer.begin(Serial1);
}

void loop()
{
  if(myTransfer.available())
  {
    char buff[40];
    
    myTransfer.rxObj(buff, sizeof(buff));
    
    Serial.println("New Data: ");
    Serial.write(buff, sizeof(buff));
    Serial.println();
  }
  else if(myTransfer.status < 0)
  {
    Serial.print("ERROR: ");

    if(myTransfer.status == -1)
      Serial.println(F("CRC_ERROR"));
    else if(myTransfer.status == -2)
      Serial.println(F("PAYLOAD_ERROR"));
    else if(myTransfer.status == -3)
      Serial.println(F("STOP_BYTE_ERROR"));
  }
}

For theory behind robust serial communication, check out the tutorials Serial Input Basics and Serial Input Advanced.

Am I right in thinking I record the millis at startByte being found then when I get to endByte I can record millis, take one from the other and I then know the timing or is that just the send timing, not each of the block timing?

not sure what you mean by block timing, but sounds right.

if you precisely (usec) capture when the "start bit" starts, when the line goes from high to low and when the "stop bit" ends, when the line goes low to high, take the difference and divide by the total number of bits -1 should give you the precise bit rate. RS-232 allow up to 2% error in clock speed.

Robin2:
I don't understand what you have in mind by "timing". For serial communication the baud rate determines the timing.

...R

Perhaps I wasn't quite explaining myself.
I want to effectively "replay" the data back so I need the timing to be correct, the timing I'm on about is between the binary packets in between the startByte and endByte.

So to illustrate I want to replay:

startByte
10111011
11
0
0
0
11
11111101
endByte

Getting the timing in between each of the binary values correct as I'd assume it's important.

@Power_Broker unfortunately I've got no control over the two existing Atmel processors so I'm eventually going to man-in-the-middle the serial data, grab what I want and then send on what I want.

I've tried modifying this code to write almost at the same time as reading but this doesn't seem to work and I suspect it's due to timing.

#include <SoftwareSerial.h> // This is the one for ESP8266 specifically
SoftwareSerial swSer1(11, 12, false); // RX port then TX port
SoftwareSerial swSer2(9, 10, false); // RX port then TX port
#define MAX_MILLIS_TO_WAIT 8000  //How long to wait for serial data to avoid hanging code
byte startByte = 188;                     // <- start byte of serial 10111100
byte endByte = 253;                       // <- stop byte of serial 11111101
boolean newData = false;
byte ch;
byte ser_indata[10]; // byte array for serial binary data

void setup() {
  Serial.begin(115200);
  delay(3000);
  Serial.println(F(" Debug serial binary output"));
  swSer1.begin(9600,SWSERIAL_8N1);
  swSer2.begin(9600,SWSERIAL_8N1);
}

void loop() {
  static boolean recvInProgress = false;
      static byte ndx = 0;
      uint8_t rc;
    unsigned long starttime = millis();
    while (swSer1.available() > 0 && newData == false && ((millis() - starttime) < MAX_MILLIS_TO_WAIT)) {      
      rc = swSer1.read();
      swSer2.write(rc,BIN);
      Serial.println(rc,BIN);
      if (recvInProgress == true) {
        if (rc != endByte) {
          ser_indata[ndx] = rc;
          ndx++;
        } else {
          end_byte=runtime;
          recvInProgress = false;
          ndx = 0;
          newData = true;
          swSer1.flush(); // flush now we have a valid pack of data?
        }
      } else if (rc == startByte) {
        start_byte=runtime;
        recvInProgress = true;
      };
    };
    if (newData == true) {
      Serial.println("Data found:");
      for(int i = 0; i < sizeof(ser_indata); i++)
      {
          Serial.print(ser_indata[i]);
          Serial.print("  =  ");
         Serial.println(ser_indata[i],BIN);
      };
      newData = false;
    };
}

No luck, not sure how to debug/figure where the serial code part is going wrong though!

AndyB (that's my brother's name, use your own name :o )

Getting the timing in between each of the binary values correct as I'd assume it's important.

Maybe I have not understood but I think you are concerned about nothing. All you need to know is the baud rate and the actual data you want to record and re-send. The RS232 hardware takes care of the necessary timing. There is no need to record the timing or to try to replicate it.

Either that or I have completely misunderstood what you are trying to do.

If you are using the hardware uart of the ATmega328, it will be very hard to capture the timing exactly, as the uart is hardware buffered. The best way to get the timing exact is to look at the waveform on an oscilloscope. In fact, some oscilloscopes can also decode the data, such as the SDS1202X-E from Siglent. If you want to replicate the data using an ATmega328, you will probably want to use a bit-bang method.

It seems the OP wants to fake out the receiver by sending the characters at precisely the times that the original transmitter used.

This is unusual, which is why everyone is all on the baud rate &c. Unusual but not impossible - it would, for example, make an interesting security feature, like enter your password, but neither too fast nor too slow.

Is there anything in the communication that gives a clue that supports the assertion that the inter-character timing is critical? Any sign of flow control or other acknowledgment or hand shaking? Could there be another line of communication besides the serial TX and RX?

Whatever. It doesn't seem like it would be hard to get the timing close, very close. Set up two arrays or a struct to capture the characters and the micros() time that they arrive. Gather enough characters so you have a full packet.

You can "play back" that sequence using the time stamps to wait between sending each character accordianly.

a7

trojan horse?

andyb2000:
So to illustrate I want to replay:

startByte

10111011
11
0
0
0
11
11111101
endByte




Getting the timing in between each of the binary values correct as I'd assume it's important.

That suggests to me that you want to record the instant at which each separate byte of the message is received.

I reckon if you want to do that you are going to have to modify the Serial library to get the USART interrupt to record the value of micros() at which the interrupt is triggered.

To be blunt I just can't understand why the time interval between bytes matters. I could more easily understand it if you wanted to identify the time interval between complete messages which would be easy to do.

...R

Robin2:

I reckon if you want to do that you are going to have to modify the Serial library to get the USART interrupt to record the value of micros() at which the interrupt is triggered.…

I reckon I don't see why. Sit on Serial.available, grab the char when it "becomes" available, note the time, viz:

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

char theChars[50];
unsigned long theTimes[50];
int theIndex;

void loop() {
  while (!Serial.available());

  theChars[theIndex] = Serial.read();
  theTimes[theIndex] = micros();

//  Serial.print(theChars[theIndex]); Serial.print(" @ "); Serial.print(theTimes[theIndex]); Serial.println(""); 

  theIndex++;   // doh!

  if (theIndex > 12) {
    for (int ii = 0; ii < 12; ii++) {
      Serial.print(ii); Serial.print(" ");
      Serial.print(theChars[ii]); Serial.print(" @ "); Serial.print(theTimes[ii]); Serial.println(""); 
    }
    for (; ; ); // die here, we done.
  }

}

Seems to work OK. YMMV. I commented out one line of printing as it would, er, take time. So Igatgher twelve chars and print a little report.

I note the OP is using the software serial, I wonder aloud if that presents any challenge in this circumstance.

a7

alto777:
I reckon I don't see why. Sit on Serial.available, grab the char when it "becomes" available, note the time, viz:

this captures the time that the char was received regardless of bit rate, not the bit timing.

if loop() simply monitored the state of the RX bit and measures the start and end times of the "start" and "stop" bits can provide the info needed to precisely determine the bit rate.

i don't think it would take much to capture the bits as well if you know the nominal bit rate

gcjr:

this captures the time that the char was received regardless of bit rate, not the bit timing.

The bit timing is not the issue, it is the times at which the characters are sent.

Am I on the right track and is it possible to buffer this, record the timing and then send it in a separate for loop later?

As I read it, you are on the right track and it looks to be entirely possible.

a7

alto777:
The bit timing is not the issue, it is the times at which the characters are sent.

If you are correct it is a very strange requirement and the OP has not explained why the timing of individual characters matters.

And, strictly speaking, we are talking about the times when the characters are received.

...R

andyb2000:
I've been sniffing a serial communication between two atmel chips, so I've determined they're at 9600baud and have managed to identify they are sending 8-byte binary data with defined start and end bytes so from that I can determine the total data frame of 10 'blocks'.

Serial data over UART Port goes frame-by-frame; where, a typical frame consists of: 1-Start Bit (LOW), 8-data bit for a character, 1-Stop Bit (HIGH). Fig-1 depicts a frame for transmit/receive of the character A.
asyncFrame.png
Figure-1:

So when I run a simple serial monitor code I see the data simply outputting the raw data line this. Here is my simple code (Running on ESP8266 lolin)

Say, something in a clear way like this: MY ESP8266 NodeMCU is connected with another Arduino using soft UART Port. When I send data from the InputBox of the Serial Monitor of ESP8266, the data apperas nicely on the OutputBox of the Serial Monitor.

So this looks like its working. Now what I want to do is 'record' a set of data (it just repeats the code constantly) and play it back, but timing is then important so I need to record the timing used on serial too and play it back at the right speed/timing, this is starting to give me a headache!

Where do you want to record the message being sent from the Serial Monitor? Do you want to record it on the SD card and then play back on the Serial Monitor?

Am I right in thinking I record the millis at startByte being found then when I get to endByte I can record millis, take one from the other and I then know the timing or is that just the send timing, not each of the block timing?

There is nothing such as startByte; look at Fig-1, there is Start Bit and Stop Bit. These two bits mark the beginning and ending of a frame. Do you want to detect them?

asyncFrame.png

GolamMostafa:
There is nothing such as startByte

I think the OP is using start byte in the same sense that it is used in Serial Input Basics

...R

Yes! I understand that a message (a number of frames/a string) may begin with a startByte (the preamble) and end with a stopByte (the postamble).

Hi everyone, sorry I'm just catching up on all the discussions.
Firstly, apologies I've probably used the wrong terms which has confused the issue here.

I have the following setup:

Atmel chip --> serial --> Atmel chip

I'm sniffing the data at the "serial" in the middle, so I have gnd common and using the software serial RX to watch the data stream. That works and I'm seeing the binary data when using 9600-8-n-1 baud so thats correct.

Now, the data is like this:
fixed startByte (preamble) and end with stopByte (postamble).
Inbetween there is a fixed number of values being sent.

I've used my logic analyser to grab a snip of the data so you can see which should hopefully help.

So startByte = 1011101
stopByte = 11111101

And then inbetween we get
00000011
00000000
00000000
00000000
00000011

And this repeats.

So back to what I'm trying to do, I'm trying to use softwareserial to transmit these back, I'm assuming the Atmel chips aren't just watching for the startByte, data then stopByte, I'm assuming they're using timing for validity too as I just cannot seem to 'playback' the data with any success.

Maybe I am overcomplicating. I'm going to test this out with a serial port, terminal and try just throwing data manually and see what the device does.

andyb2000:
I'm assuming the Atmel chips aren't just watching for the startByte, data then stopByte,

It would be very strange if they were doing anything else.

Why don't you post the programs from the Atmel chips?

...R