How to create communication protocol with byte stuffing?

Go To Last Post
26 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi everyone!

 

I am struggling for some time with  a problem that concerns an Atmega324PA and a PC.I was asked to create a communication protocol  between the 2 of them but for some reason either the PC part doesn't work,either Atmega.I am using Atmega as a console as it has a joystick and a matrix display attached to it.My problem comes when I want to send the input values from the PC to the console because it won't read any value inputed from the keyboard although I have already specified both the PC and console side the values which are used as input for moving the paddles(i am using it in a ping-pong game with FreeRTOS).This is how the code looks like:

//on the console side
void Input_PC(void *pvParameters)
 {
	 // The parameters are not used
	( void ) pvParameters; 

	 #if (configUSE_APPLICATION_TASK_TAG == 1)
	 // Set task no to be used for tracing with R2R-Network
	 vTaskSetApplicationTaskTag( NULL, ( void * ) 1 );
	 #endif
	 uint8_t Input_PC=0;

	 _x_com_received_chars_queue=xQueueCreate(_COM_RX_QUEUE_LENGTH,(unsigned portBASE_TYPE)sizeof(uint8_t));//creating queue for input values

	 init_com(_x_com_received_chars_queue);

	 while(1)
	 {
		 xQueueReceive(pc_queue,&Input_PC,portMAX_DELAY);//sending the PC queue values 
		 pc_value = Input_PC;
		 for(int i=4;i<10;i++)
		 {
			 if(Input_PC==75)//move left on PC
			 {

					 if(twod[i][13]==1 && twod[i-3][13]==twod[0][13])
					 {

						 twod[i][13] = 0;
						 twod[i-1][13] = 1;
						 twod[i-2][13] = twod[i-1][13];
						 twod[i-3][13] = twod[i-2][13];
						 break;
					   }
					 }else if(Input_PC==77)//move right on the PC
					 {
						if (twod[i][13] == 1 && twod[i+3][0]==twod[9][13])
							 {
								 twod[i][13] = 0;
								 twod[i+1][13] = 1;
								 twod[i+2][13]= twod[i+1][0];
								 twod[i+3][13]= twod[i+2][0];
								 break;
							 }

						 vTaskDelay(100);
					 }
			 }
		}
	 }
	 

and this is the byte stuffing method along with a CRC method that checks if the data send has any errors or not:

//communication protocol console

void COM_Protocol(void *pvParameters)
{
( void ) pvParameters;
#if (configUSE_APPLICATION_TASK_TAG == 1)
// Set task no to be used for tracing with R2R-Network
vTaskSetApplicationTaskTag( NULL, ( void * ) 8);
#endif

uint8_t index = 0;
uint8_t unpacked_message[20] = {0};//this value "20" was initialy 10 but for some reason it will not work with 10 and i changed it to 20
uint8_t received=0;
uint8_t is_empty = 0;

while(1)
{
	if(!xQueueReceive(_x_com_received_chars_queue, &received, portMAX_DELAY))
	{
		vTaskDelay(200);
	}

	switch (state) {//start creating the cases
		case IDLE://if the state is idle and the byte received is flag,the pointer will look next for the data

		is_empty = 0;
		if (received == FLAG)
		state = DATA;
		break;

		case DATA://if the data is next,then the ESCCHAR found in it will set the state to ESC

		if (received == ESCCHAR )
		{
			state = ESC;

		}
		else if (received == FLAG && is_empty != 0)//if there is a FLAG received and the queue is not empty,then the state will be idle and the message sent 
//will be checked using the method check_message which will ack or unack the message depeending on what it receives
		{
			state = IDLE;
			check_message(unpacked_message, index);
			index = 0;
		}
		else if (received == FLAG && is_empty == 0)//if the queue is empty then the message is marked as received 
		{
			state = IDLE;
		}
		else
		{
			unpacked_message
= received; is_empty = 1; index++; } break; case ESC://in the ESC state the message is received and the queue pointer is set to 1.The state will change to DATA unpacked_message
= received; is_empty = 1; index++; state = DATA; break; } vTaskDelay(50); } } void check_message(uint8_t message[], uint8_t length)//the KEYBOARD_INPUT is the operation code for the pc input. { if(message[1] == s_nr)//check if the message index is the sequence nr { //if(message[length-1] == getCRC(message, length-1)) //if(message[length-1] == 5) if(message[length-1] == CRC_calculation(message, length-1))//check if the message has any errors { uint8_t to_push; if(message[0] == KEYBORD_INPUT)//push the instruction from the pc input to the queue { to_push = message[2]; xQueueSend(pc_queue, &to_push, portMAX_DELAY); //should be message[2] once the seq number and op_code are added answer[0] = FLAG; answer[1] = ACK_OP_CODE;//ackowledge the message answer[2] = s_nr; answer[3] = FLAG; com_send_bytes(&answer[0], 1); com_send_bytes(&answer[1], 1); com_send_bytes(&answer[2], 1); com_send_bytes(&answer[3], 1); s_nr++; } } else { answer[0] = FLAG; answer[1] = NACK_OP_CODE;//unacknowledge the message answer[2] = s_nr; answer[3] = FLAG; com_send_bytes(&answer[0], 1); com_send_bytes(&answer[1], 1); com_send_bytes(&answer[2], 1); com_send_bytes(&answer[3], 1); } } }

Now I am sure that the console side works well since when i declare the tasks and I run it I won't have any problem with the paddle moving.When I run the code in Visual Studio though and I press "A" and "D" for left and right,nothing will work as it will not read the input from the keyboard although I am using an operation code for the keyboard input:

First I open the port "COM4" to use it as a serial to transimt data:

//communication from the PC side


//method used to open the serial connection with the board
void open_port()
 {

	 DCB dcbSerialParams = { 0 };
	 COMMTIMEOUTS timeouts = { 0 };

	 fprintf(stderr, "Opening serial port...");

	 // In "Device Manager" find "Ports (COM & LPT)" and see which COM port the game controller is using (here COM4)
	 hSerial = CreateFile(
		 "COM4", GENERIC_READ | GENERIC_WRITE, 0, NULL,
		 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	 if (hSerial == INVALID_HANDLE_VALUE)
	 {
		 fprintf(stderr, "Here Error\n");
		 return;
	 }
	 else fprintf(stderr, "OK\n");

	 // Set device parameters (115200 baud, 1 start bit,
	 // 1 stop bit, no parity)
	 dcbSerialParams.DCBlength = sizeof(dcbSerialParams);
	 if (GetCommState(hSerial, &dcbSerialParams) == 0)
	 {
		 fprintf(stderr, "Error getting device state\n");
		 CloseHandle(hSerial);
		 return;
	 }

	 dcbSerialParams.BaudRate = CBR_115200;
	 dcbSerialParams.ByteSize = 8;
	 dcbSerialParams.StopBits = ONESTOPBIT;
	 dcbSerialParams.Parity = NOPARITY;
	 if (SetCommState(hSerial, &dcbSerialParams) == 0)
	 {
		 fprintf(stderr, "Error setting device parameters\n");
		 CloseHandle(hSerial);
		 return;
	 }

	 // Set COM port timeout settings
	 timeouts.ReadIntervalTimeout = 50;
	 timeouts.ReadTotalTimeoutConstant = 50;
	 timeouts.ReadTotalTimeoutMultiplier = 10;
	 timeouts.WriteTotalTimeoutConstant = 50;
	 timeouts.WriteTotalTimeoutMultiplier = 10;
	 if (SetCommTimeouts(hSerial, &timeouts) == 0)
	 {
		 fprintf(stderr, "Error setting timeouts\n");
		 CloseHandle(hSerial);
		 return;
	 }
 }

The inputTask will take the values inputed from the keyboard and will send then on the console side:


/*the input task will wait for an input from the keyboard and then it will send the operation code along with the length of the operation to the console.The method
prepare_for_sending will check the message for errors and it will perform a byte framing */
static void inputTask(void *pvParameters)
 {
	 uint8_t *to_insert;
	 uint8_t value;

	 for (;;)
	 {
		 input = (uint8_t)getch();
		 value = input;
		 printf(" /n The value is: %d", value);
		 if ( input == 75 || input == 77)
		 {
			 to_insert = &input;
			prepare_for_sending(to_insert , 1, KEYBORD_INPUT_CODE);
			 input = 0;
			 //prepare_for_sending(to_test, 6);
			 //xQueueSend(input_queue, (void*)&to_insert, portMAX_DELAY);
		 }
		 vTaskDelay(300);
	 }
 }

There are also some methods that I found interesting suggested by some of my colleagues for sending the message and cheking it for errors with CRC,I format it with an operation code and i use byte framing to send it.The method receiveTask it's the one that concerns me as there is where the magic happens.The method will check the byte stuffing to see if it is correct or not

/*This task will look similar with the COM_Protocol on the console side as it receives the message and checks if it has been sent correctly or not.*/

static void receiveTask(void *pvParameters)
 {
	 uint8_t i = 0;
	 uint8_t *byte_to_receive = &i;//a pointer that is used as value for every byte from the frame and it checks if it has the value of flag escchar

	 uint8_t is_empty = 0;
	 uint8_t unpacked_message[20];//this one I don't really know why is 20.I tried playing with the values but without any luck.
	 uint8_t index = 0;
	 uint8_t check = 1;

	 uint8_t bytes_to_receive[10];

	 /* Prevent the compiler warning about the unused parameter. */
	 (void)pvParameters;
	 for (;; )
	 {

		 //Read text
		 DWORD bytes_read, total_bytes_read = 0;

		 ReadFile(hSerial, byte_to_receive, 1, &bytes_read, total_bytes_read);
		 fprintf(stderr, "%d bytes read\n", bytes_read);

		 fprintf(stderr, "Text read:  %d\n", *byte_to_receive);

		 fprintf(stderr, "End\n");

		 switch (state) {
		 case IDLE:

			 is_empty = 0;
			 if (*byte_to_receive == FLAG)
				 state = DATA;
			 break;

		 case DATA:

			 if (*byte_to_receive == ESCCHAR)
			 {
				 state = ESC;

			 }
			 else if (*byte_to_receive == FLAG && is_empty != 0)
			 {
				 state = IDLE;
				 check = check_message(unpacked_message, index);
				 if (check == 0)
				 {
					 DWORD bytes_written, total_bytes_written = 0;
					 fprintf(stderr, "Sending bytes...");

					 if (!WriteFile(hSerial, to_send, to_send_size, &bytes_written, NULL))
					 {
						 fprintf(stderr, "Error\n");
						 CloseHandle(hSerial);
						 return 1;
					 }
				 }
				 for (int i = 0; i < 20; i++)
				 {
					 to_send[0] = 0;
				 }
				 check = 1;
				 index = 0;
			 }
			 else if (*byte_to_receive == FLAG && is_empty == 0)
			 {
				 state = IDLE;
			 }
			 else
			 {
				 unpacked_message
= *byte_to_receive; is_empty = 1; index++; } break; case ESC: unpacked_message
= *byte_to_receive; is_empty = 1; index++; state = DATA; break; } vTaskDelay(100); } // Close serial port fprintf(stderr, "Closing serial port..."); if (CloseHandle(hSerial) == 0) { fprintf(stderr, "Error\n"); return 1; } } uint8_t getCRC(uint8_t message[], uint8_t length) { uint8_t crc = 0; for (int i = 0; i < length; i++) { crc ^= message[i]; for (int j = 0; j < 8; j++) { if (crc & 1) crc ^= POLYNOMINAL; crc >>= 1; } } return crc; }

 

Can someone please help me understand what is happening with the code and why the values from the keyboard will not make the paddle move?

Best regards,

M

Last Edited: Wed. Jan 23, 2019 - 04:57 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That's a lot of code to wade through - do you have some design documents? Flow chart/diagrams etc?

 

I can see that it seems to involve ESC-CommandByte but perhaps you can explain the desired byte sequence in the protocol exchange? What do "packets" actually look like?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:

That's a lot of code to wade through ...

 

//Especially without any comments

#1 Hardware Problem? https://www.avrfreaks.net/forum/...

#2 Hardware Problem? Read AVR042.

#3 All grounds are not created equal

#4 Have you proved your chip is running at xxMHz?

#5 "If you think you need floating point to solve the problem then you don't understand the problem. If you really do need floating point then you have a problem you do not understand."

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you both for your reply.Which part exactly you don't understand? If i understand correclty what you say regarding the byte sequence,the first sequence is the operation code then the ack or unack of the data the sequence nr and the flag as far as i know.I also procured a part of the code from one of my colleagues in order to understand the protocol better but i don't have any luck with it since i am a very bad programmer in which concerns coding.So the console protocol part i understand perfectly,but the client side protocol is a mistery for me...i don't know how it should work or if it works as a receiver.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If you could something we could build on a PC, that would be a great help to us freaks that enjoy obscure problems of this type.

 

My guess though is that if you do just that you'll end up finding the bug yourself.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I will make a task diagram for the communication protocol and upload it.I will also upload the full PC protocol part i have.I really want to understand why is not working as well,but i don't have any errors when i establish the connection between the two parts.Is there any tool to help me check the communication protocol?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

maria1571 wrote:
Which part exactly you don't understand?
Assume you know nothing about this project - if someone just dumped all that code in front of you (that you weren't familiar with) how quickly/easily would you be able to work out what's going on?

 

That's exactly what you are asking us to do here. This highlights an essential of software engineering - code should be structured to be easy to follow/read - your's isn't.

 

As I say one thing that could help is a diagramatic explanation of the protocol. I'm just going to google an example from the internet at random:

 

Image result for example format of tcp packet header

Both you and I can look at that and immediately know what to expect here (it's a TCP header by the way). While this is shown as a break down of 32 bit words if you take it a byte at a time (assuming it's sent big endian!) then the first byte has a "version number" in the upper 4 bits and an "IHL" (I'm guessing "Internet Header Length" perhaps?) in the lower 4 bits. The next whole byte contains something called DCSP in the upper 6 bits and "ECN" in the lower 2 bits and so on.

 

So now I know exactly what data stream to expect in this sequence. In fact I could probably put together some sender or receiver code based purely on this diagram alone. This is true proof that a picture is worth 1,000 words. What's more if someone (like you did above) gave me accompanying C code and said "it sends/receives this protocol" I could have a pretty good stab of saying whether that was actually true/false and, if false I could probably tell them something like "yeah, but your ECN bits aren't the bottom two in byte 2 - you have inadvertently encoded them into the top bits of byte 3" or whatever.

 

Just dumping a whole load of source that you maybe understand (but no one else does and you won't either in 18 months) and asking "what's wrong here?" is very unlikely to deliver any useful result.

 

As I say, you can't underestimate the power of design documentation. Presumably whoever devised your protocol in the first place didn't just make it up as they went along writing C code to first send it then more C code to receive/decode it? I imagine they sketched out some notes on a pad to figure out what the data stream would look like - then wrote C to implement this - it's those design notes that are absolute dynamite when it then comes to maintaining the code like this. In fact if the code/notes are not simply kept together in a source repository then a very good idea is to use Doxygen to encode the notes (or at least a salient summary) into the code itself so then the single .c/.h file is effectively self documenting. That documentation process goes further in that, while there might be a complete summary of the data stream given, the implementing code might also benefit from individual comments like "and this is where the two bit ECN field is formed" or whatever.

 

Always write your code with the NEXT reader in mind.

 

(IMAO)

Last Edited: Wed. Jan 23, 2019 - 03:35 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Super!Thanks for the heads up.I will also comment the code lines and I will try to make it as structured as possible.I know there's not only me who has this problem and I really want everybody who sees this question to gain sth out of it when he/she will read it.I will redo the question and add a task diagram and comment the code uploaded.Thank you for your support!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Last Edited: Wed. Jan 23, 2019 - 04:57 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The state diagram shows how would the protocol look like.I would really appreciate some help in figuring the code since I am not a very good programmer when it comes about protocols and especially Atmega.Thank you for your support clawson!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why are you making it so complicated?  You are using a USART (serial port), correct?  Simply send requests for what data you need (ex: 

send

JXVAL?, reply:  237,

JYVAL?  reply: 534 

VOLTS?  reply: 5.66

DISPMSG  Hello There reply: OK

Easy to debug & see what is going on.

You can use parity, or add a check character or CRC bytes, once you have that working.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So the packets are ESC-OP-SEQ-Payload-CRC-ESC ?

 

I don't understand in that, if the Payload can be more than 1 byte how do you know when the payload ends and the CRC occurs? Surely you want something like:

 

ESC-OP-SEQ-LEN-Payload-CRC-ESC

 

or

 

ESC-OP-SEQ-Payload-(term char that cannot otherwise occur within payload)-CRC-ESC

 

The payload is then a bit like the difference between "Pascal strings" and "C strings". In C we know that strings terminate with 0x00 which is a character that otherwise cannot occur in the string. Pascal does this differently, conveying the length separately so that the payload can contain ANY character.

 

Like avrcandies I do wonder is this is over-engineering? Is the link really so unreliable (wireless?) that there may be packet corruption and hence the need for CRC? Also why "sequence number"? That is usually used in systems (like internet protocol) where packets can take different routes to a destination so may arrive out of sequence and therefore need numbers to show the sequence they should be digested in (it is also part of an ACKing protocol so that you can say things like "resend packet 37")

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am using USART and I know I can do it like that,I just don't know how to implement it.I thought i would need these things with flow control,framing and so on as I procured a project from my colleagues who previously did this com protocol.I also thought that if I am using FreeRTOS, this would not be enough to transfer data properly.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It's been a pretty long time since UART connection were "unreliable" so it looks like overkill to need sequence numbers and CRCs and stuff. If there are variable length payloads I'd probably just send LEN-Payload. I suppose there is an argument to say "what happens if receiver starts to listen in after the sender has already started?" so you might want some kind of prelude that says "start of packet" before the length? An alternative is to have the sender/receiver negotiate (UART is bidirectional after all) to establish that the receiver is ready before the sender starts sending len-payload packets.

 

The bottom line is that you really need to consider what you are defending against in the first place before determining the exact details of the protocol and then run a lot of "what if" scenarios to ensure you have coped with every eventuality.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

 this would not be enough to transfer data properly

You define what is enough & proper...the uart just sends & gets (hopefully), the bytes you desire.  Sending ASCII strings is extremely easy/straightforward.  Receiving & decoding (parsing) them takes a bit more work, but is not hard. 

Error checking(& possibly correcting) can be icing on the cake, but at least get the basic messaging going.  You can look for an ack for each item sent  ("ok" ," good", "bad" , "?", checksum, etc) & simply resend if not rcvd properly.

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Both USB and CAN uses ***BIT*** -stuffing.

A simple bit stuffing protol can be that whenever 3 consecutive bits of the same polarity as send, then an extra BIT of opposite polarity is inserted.

 

I also have vague memories of 8/10 bit bit stuffing.

The idea is that every byte is expanded as a sequence of 10 bits, and those 10 bits are chosen in such a way that they have enough flanks to keep the signal synchronised, and proably also the number of ones and zero's are balanced.

 

What does wikipedia say about bit stuffing?

... Some time later... Wikipedia does not have much info on bit stuffing, a more general search might work better for you.

 

If you're unsure whether the PC or the uC firmware is wrong, an easy check is to put a Logic Analyser to the hardware (see my signature).

Alternatively, you can make the AVR code run on a PC, and then feed the bits it outputs one by one into the PC part of the program, and debug it with the usual tools.

 

It is very easy to make mistakes in CRC algorithms. Problems might arise with endianness, signed / unsigned, etc.

Write some code to encode & decode some bogus packets with both versions and compare their output.

You're also using a (16 bit) int on the AVR for counting from 0 to 8 in the AVR CRC algorithm. Not a problem, but a slight inefficiency.

 

I also consider any wired connection that goes through connector and long cables "unreliable". Just board to board interconnects I would not doubt much in reliability aspect.

But with external cables and connectors there are alsways too many possibilities to creep in, such as bad connectors or broken wires in cables.

At least a CRC is a good idea, but when you have a CRC you also need some resend algorithm.

Whether you need sequence numbers depends on the formatting of your data. What are the consequences if the same packet is received twice? Is that a problem?

Doing magic with a USD 7 Logic Analyser: https://www.avrfreaks.net/comment/2421756#comment-2421756

Bunch of old projects with AVR's: http://www.hoevendesign.com

Last Edited: Wed. Jan 23, 2019 - 05:40 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok,thank you for the info!I will try to make it simple with UART in hope it will work.

 

Best wishes,

M

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Please let me know if i can look somewhere for some examples regarding the implementation-again!I am not a very good programmer so as much as I understand the problem at hand and I am aware of the problems in my communication protocol,that is because of my lack in coding so an example would be a major help both for me and future programmers who encounter the same problem.

 

Best regards,

Maria

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
It's been a pretty long time since UART connection were "unreliable" so it looks like overkill to need sequence numbers and CRCs and stuff

.
Well the RS-485 interface is based on UART and it is said to be the most reliable wired interface of industry!
.
maria1571 wrote:
Please let me know if i can look somewhere for some examples regarding the implementation

.
I suggest take a look at "ANSI E1-20 - 2010" it is very well documented bidirectional communication protocol, you may skip the complicated sections such as "Discovery" etc. they are beyond your requirements.
Specially see "6.2 Packet Format" section. I think it would be useful in defining your own requirements.

Majid

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Look for parser, avr parser, parsing...you will find examples

 

Really, in simple cases, this amounts to string matching:

If you rcv match to "HELLO"  , reply with "SUNNY 93.12 DEG"

If you rcv match to "MYPARSER", reply with "ITS WORKING"

Rather than using a growing bunch of "if" or "case" statements, there is usually a structured array of text patterns to check against (like a "dictionary").

I've seen some folks brute-force match letter-by-letter which is really poor use of if statements!

  if letter1 is H & letter2 is E & letter3 is L & letter4 is L & letter5 is O...sound the alarm.

 

You can have "RPMxxx"  where xxx is stripped out (parsed) to determine the requested speed  (ex:  RPM396)...so you'd check for RPM followed by 3 numbers to be considered a valid command.

 

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

Last Edited: Wed. Jan 23, 2019 - 08:52 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

1. Can you explain where the "bit stuffing" is going on. I've browsed your code and cant find it. I thought the twod[][] might have something to do with it, but actually looks irrelevant.

2. Something has gone wrong with your code post

we see

void COM_Protocol(void *pvParameters)
{
...
    uint8_t unpacked_message[20] = {0}; //this value "20" was initialy 10 but for some reason it will not work with 10 and i changed it to 20
...
...
	case ESC: //in the ESC state the message is received and the queue pointer is set to 1.The state will change to DATA
		unpacked_message = received;

I would have expected:

unpacked_message
= received;

{Edit: Oh bugger My corrected line doesn't render properly in my browser. What's goin on ? I wrote unpacked_message[ index ] = received; but without extra spaces}

{Edit2: Well its not there in the HTML source, so that means the AVRfreaks code editor has fubared both the OP code and also mine}.

{Edit3: OK So when I edit the post the code is displayed as I wrote it, so it's there in the backend database just fine. It just gets served out as crap}

 

This piccy is what I am seeing:

 

 

 

Last Edited: Wed. Jan 23, 2019 - 09:14 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you for letting me know.This is the code for byte stuffing:

void COM_Protocol(void *pvParameters)
{
( void ) pvParameters;
#if (configUSE_APPLICATION_TASK_TAG == 1)
// Set task no to be used for tracing with R2R-Network
vTaskSetApplicationTaskTag( NULL, ( void * ) 8);
#endif

uint8_t index = 0;
uint8_t msg_unpacked[20] = {0};
uint8_t received=0;
uint8_t is_empty = 0;

while(1)
{
	if(!xQueueReceive(_x_com_received_chars_queue, &received, 1000L))
	{
		vTaskDelay(200);
	}
	
	switch (state) {
		case IDLE:
		
		is_empty = 0;
		if (received == FLAG)
		state = DATA;
		break;
		
		case DATA:
		
		if (received == ESCCHAR )
		{
			state = ESC;
			
		}
		else if (received == FLAG && is_empty != 0)
		{
			state = IDLE;
			chk_msg(msg_unpacked, index);
			index = 0;
		}
		else if (received == FLAG && is_empty == 0)
		{
			state = IDLE;
		}
		else
		{
			msg_unpacked
= received; is_empty = 1; index++; } break; case ESC: msg_unpacked
= received; is_empty = 1; index++; state = DATA; break; } vTaskDelay(50); } }

There are 3 cases:DATA,IDLE and ESC.Depending if an esc char is stuffed or if the esc char was already removed,then we will go to the flag state or data state.Each state does sth.The ESC state only goes either to flag either to DATA.The FLAG is for the beginning and the end of the frame.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you very so much sir for your help.I started reading it already and I can see that it offers an in depth information that is very useful for understanding the packet format of a message!

 

 

Best regards,

Maria

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It might help - all of us - if you present the application, proposed design, with a theory of operation. It does not have to be huge or elaborate.

 

Objective> In order to implement a real-time application using two 8-bit joy sticks to control a game of pong, communicating over an asynchronous serial port, a data framing protocol is a specified requirement.

 

proposed deigns> The proposed protocol is to have a start byte, arbitrary sized payload and CRC, followed by the start byte.  Multiple start bytes can be sent, with no payload.

 

Theory of operation> The protocol provides reliable registration of data frames.  An 8-bit one's compliemnt CRC is used to provide data integrity (similar to TCP).  The start/stop character will need to e escaped from the data frame interior (e.g. <ESC><ESC> is decoded as <ESC>).  Each data burst will begin with the <START> character, payload bytes, CRC and <START>.

 

Design notes> The application involves human interaction.  A sample rage of 400 Hz (2.5 ms) is selected and represents the real-time epoch of the system.  The serial port will need to sustain (fighter pilots can not feel more than 2.5 ms response times) 400 Hz (frame is <START><LEFT <RIGHT><CRC> 4 bytes, plus two potential escapes for a 6-byte frame) of 2400 bytes per second.  Using a ten-bit asynchronous framing protocol, at least 24,000 a baud rate is needed. One might argue that the video update rates beyond 24 Hz are not relevant, so a 40 millisecond real-time cycle might be sufficient.  That would call for at least 1,440 baud.

 

Given the above, where is there a need for an idle character?

 

Idle characters are part of a constant bit rate serial port, this uses an asynchronous UART (e.g. RS-232 style protocol), which generates clock only when there is data.  So, pacing is done at the source by delaying clock.

 

Perhaps it would be helpful for your to perform this sort of presentation of your design.

It would help the readers understand your issues, and would provide you are great chance to analyze and understand your project.

 

Leading the discussion to the game design might be of interest to the readers.

 

 

Last Edited: Thu. Jan 24, 2019 - 06:24 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I got confused about bit-stuffing and byte-stuffing. I got derailed in #16. From #22 I see you are trying to escape a FLAG byte that may appear in your data stream.

 

Well because of the noise & general verbosity, I don't quite know where this thread is going or even what the question is now.

Is it a bug-fixing question ?

 

BTW: It's not necessarily wrong, but your CRC code doesn't exactly match mine. You are missing an else

 

uint8_t getCRC(uint8_t message[], uint8_t length)

{

    uint8_t crc = 0;

 

    for (int i = 0; i < length; i++)

    {

        crc ^= message[i];

        for (int j = 0; j < 8; j++)

        {

            if (crc & 1)

                crc ^= POLYNOMINAL;

            else /* I'VE ADDED THIS */

                crc >>= 1;

        }

    }

    return crc;

}

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

N.Winterbottom wrote:
I got confused

 

It seems that we are doing a homework assignment.

 

I've been trying to offer good process...

 

The class is probably about communications and assignment is related to a circuit type connection where the clock does not stop.  In such a system if the data underflow, something needs to be done to effect flow control something needs to be transmitted in the void time.  The implied solution was to send an idle pattern.  To keep the idle pattern synchronized, there needs to be some sort of a framing protocol.