Go Down

Topic: How to check for T1.5 in Modbus RTU protocol? (Read 462 times) previous topic - next topic

test159357

Hello all!!! First post here.

I hope I'm posting this in the right place (Not sure if should be in the Project Guidance, Programming Questions or any other tbh) but anyways. Also sorry if this turns out to be lengthy as I try to explain the issue I'm having.

I am trying to create a Modbus RTU Master module . I'm aware of the various libraries that exist for this purpose. Although after reading the specification and implementation guide available from here
http://www.modbus.org/docs/Modbus_over_serial_line_V1.pdf
and specifically bottom of page 13 I came across the t1.5 timer. That timer is used to check if the bytes of a frame do not have a silent interval of more than 1.5 characters, were t1.5 = 16500000/baud rate.

Since I'm trying to implement this on an Arduino Uno I will be using either the hardware serial or the alsoftserial library to create a software serial to push the data with a format of 8E1 or 8N2 to an RS485 module that uses the MAX485 chip.

After some further studying I came across the idea of logging the time when UART Rx Complete flag is set through an ISR and then compare it with the next time the flag is set. However, I don't know if this could be done without causing other issues.

Also, I'm not sure if this check is needed while I will be sending the frames. In my understanding I think I don't need it because the serial transmission happens using interrupts and thus no other interrupts are happening apart from the ones updating the Timers.

TLDR: How can I check if the inter-character delay while receiving Modbus frames is less than or equal to t1.5 with an Arduino Uno?

Any suggestions are extremely appreciated. Thank you for your time :)
Le tester

sherzaad

#1
Jan 13, 2018, 11:39 pm Last Edit: Jan 14, 2018, 12:16 am by sherzaad
you could use micros().

so in your receiving routine have something like this:


Rx_buffer[ x ] = Rx_in;

timestamp_buffer[ x ] = micros();

then in your main routine where you are reading received data you can check for the time between 2 received frames.

so along those lines:

if(Serial.available()){
  newtime = timestamp();
  datain = Serial.Read();
  timediff = newtime - oldtime; //where oldtime is the previous received timestamp
  oldtime = newtime;

}

test159357

First of all thanks for the reply and the kind suggestion.

Will this provide an accurate result?

I'm concerned about even if the data come late at the UART buffer register then after being transferred to the Hardware serial circular buffer the silent interval won't be seen since in the way you suggested I'm checking the time between fetching data from the circular buffer of Serial and not UART. I'm not exactly sure how the transfer from the register to the buffer happens. I did study a bit the HardwareSerial libraries but it was a bit too much to try to understand just to perform this thing.

I assume the process is something like: data to the Rx pin->data to the receive shift registers->data to the UART buffer register->IRQ->ISR->data to the circular buffer of serial->IRET.

My point is that even if the data come a bit late at the UART buffer then until it gets stored in the circbuff there will always be there the next byte. So don't I have to perform the check at a lower level from what you suggested? Or am I totally overthinking it? I don't know whether when the MCU is handling an interrupt also halts the pushing of characters in the UART buffer.
Le tester

sherzaad

#3
Jan 14, 2018, 04:17 pm Last Edit: Jan 14, 2018, 04:18 pm by sherzaad
Will this provide an accurate result?
Honestly I don't know since I cannot test it but based on a previous project I did with similar requirement I should think it would.

I've added a screen shot from the doc you mention in your original post. (it hurt my head reading your reply but I think I got the point!  ;) )



so each frame has N number of bytes and the spacing between them should be no more that 1.5 characters (not sure on this point but I taking it that 1 character is 1 byte). so using my methold the interspace between the frame bytes should be not more that ((1.5 + 1)*10bits)/baudrate, +1 since the timestamp is taken AFTER the byte is received, 10bits assuming 8N1 UART.

same principle for the space between frames.

Of course this does not take into account the processing time which you can compensate for by either crunching the numbers for the clock cycles used or by trial and error!

Hope that helps and that I did not miss the point!  :D

ps I do have a hardware serial library which I modified for a similar purpose but I wonder with you can get a suitable buffer size for you purpose...


test159357

Hehe...Sorry for the headache I cause you.
Let me see if I get this straight.
Assuming 8E1 or 8N2 (because Modbus RTU either using parity or not the character is always 11 bits, substituting the non existing parity with an extra stop bit)
and a baud of 115200 (just for the sake of not using the standard 19200 baud)
we get:
11500 bits per second and since each character is 11 bits then we have 10472 characters per second
or 10472 characters per 1000 milliseconds.
Therefore for 1.5 characters we get 0.14324ms or 143.24us.

So manipulating your suggestion I could always read the timestamp before the character is received and stick with the 1.5 characters instead of reading it after and using 2 characters (1+1.5). Correct me if wrong plz.
Le tester

sherzaad

#5
Jan 15, 2018, 06:15 am Last Edit: Jan 15, 2018, 08:32 am by sherzaad
Therefore for 1.5 characters we get 0.14324ms or 143.24us.

So manipulating your suggestion I could always read the timestamp before the character is received and stick with the 1.5 characters instead of reading it after and using 2 characters (1+1.5). Correct me if wrong plz.
Yes, that sounds right, though timestamping when the serial ISR is triggered rather than after receipt of character may be more challenging... not so hard if using altSoftwareSerial but harder if you intend to use hardware serial! :)

Bear in mind that micro() is updated in multiples of 4 meaning that luckily for you the next value is 144us! you can #define T1_5 143, then check for newtime - oldtime > T1_5

if you want greater time accuracy you may want to implement you own timer using one of the available hardware timers

test159357

Quote
not so hard if using altSoftwareSerial but harder if you intend to use hardware serial!
To be honest I was planning to use altSoftSerial so hurray for me.  :smiley-lol:

Quote
Bear in mind that micro() is updated in multiples of 4
I didn't know that. I never bothered to look for it an I assumed it was updating every us. The things you learn by luck. Thanks for enlightening me.

I do believe though that this updating rate is enough for me. I would be kind of overkill to check every us even if this results in discarding the whole frame and thus the other device has to resend it.

Do you by any chance have something I can build on and not trying to reinvent the wheel? (talking about the altsoftserial or the hardware serial libraries)
Le tester

sherzaad

Do you by any chance have something I can build on and not trying to reinvent the wheel? (talking about the altsoftserial or the hardware serial libraries)
dont have a modified altsoftserial library but have one for hardware serial. you will need to replace the original files in the arduino IDE folder with these to use it. timestamping in my library is done after receipt of character.

hope it helps.

test159357

#8
Jan 18, 2018, 02:22 am Last Edit: Jan 18, 2018, 06:57 pm by test159357 Reason: Mistake in attachment
I can not express how grateful I'm to you. You saved me from a lot of hassle while still helping me gain much knowledge.

I "think" I've successfully modified AltSoftSerial library by mimicking the technique you used in yours.
Also, the library version I'm using is also pre-modified (not by me) to also be able to use different parities and data bits like 8N1,8E1,8O1,8N2,8E2,8O2 etc.

Btw, I was banging my head on the wall because it was not giving correct results with a baud of 115200. Then I tried 74800 and still nothing. But it worked for 19200. So then I decided to read again the manual at https://www.pjrc.com/teensy/td_libs_AltSoftSerial.html. Until I reached the part where it says
Quote
AltSoftSerial & SoftwareSerial Usable Speed
...and there it was...the magic number 57600.

Anyways...I attach the library for people to crosscheck the functionality or just use it in their project.

Thank you again for the major help you provided. May the force be with you!

EDIT: I have made a stupid error on the function returning the micros timestamp (instead of uint32_t I used uint8_t). I fixed it and it now works as it should. Please consider downloading the fixed version of the library
Le tester

Go Up