Protocolo de Comunicação STX-ETX

Em agosto de 2019 publiquei o artigo “FreeMODBUS – Apresentação & Port” onde apresentei uma breve introdução sobre o Modbus, FreeModbus e um “port” que desenvolvi para a placa  NXP Freedom Board KL25Z. Porém, o protocolo Modbus para algumas aplicações, pode trazer um excesso de complexidade desnecessária. Por exemplo, aplicações simples entre mestre-escravo ponto a ponto. Como alternativa de protocolo de comunicação mais simples temos o padrão STX-ETX.

O padrão STX-ETX, trata-se de um protocolo flexível e fácil de implementação dentro do padrão de sistema de segurança presentes no mercado. É um protocolo orientado a caractere, segmentação por delimitadores de dados e comprimento de frames variáveis.

Nota I: Infelizmente na minha pesquisa para escrever esse artigo não encontrei informação específica sobre a teoria desse padrão. Por conta disso vou apenas concentrar na implementação.

Nota II: Por se tratar de protocolo flexível e amplamente utilizado, existe bastante variações. A implementações que irei apresentar neste artigo, é algoritmo que costumo utilizar em meus projetos.

 

Implementação do Protocolo de Comunicação STX-ETX

 

As mensagens do protocolo de comunicação STX-ETX são formados por frames (conjunto de byte que forma a mensagem). O frame é divido em três grupos; 

Cabeçalho da mensagem (Message Header): onde é composto pelos bytes de sincronismo (SYN) e o byte STX. Esse conjunto de bytes tem como objetivo sinalizar o envio de um novo frame.

Conteúdo da mensagem (Message Content): para a implementação que desenvolvi o conteúdo da mensagem é formada por;

  • MSG Byte Number: Número de byte que é compõem o MSG Data[ n ], que varia de zero a quatro bytes.
  • Command / Status: é número do comando ou número registro, para as mensagens enviadas pelo master, as mensagens de retorno do Slave este byte é utilizado como status, para notificar master se recebimento da mensagem. 
  • MSG Data [ ]: bytes destinado ao conteúdo da informação.
  • Checksum: é o código usado para verificar a integridade de dados transmitidos.

Byte ETX: é byte que sinaliza o fim do frame. 

A seguir temos figura que ilustra a composição do frame da mensagem:

 

O Protocolo de Comunicação STX-ETX
Frame da Mensagem

 

Implementação

 

A implementação desenvolvida é dívida entre Master (mestre) e Slave (escravo). O algoritmo desenvolvido para Master, é uma aplicação desktop. Essa aplicação contém o algoritmo do protocolo e a interface de interação com o usuário, essa interface é simples baseada em terminal, onde permite que o usuário construa a mensagem a ser enviada ao dispositivo Slave. 

A seguir temos o código fonte do master onde contém o algoritmo que constrói a mensagem, o restante do código fonte do master pode ser consultado no Github.

 

/*
 * stx_etx.c
 *
 *  Created on: 8 de jan de 2020
 *      Author: Evandro Teixeira
 */


#include "stx_etx.h"

/**
 *
 */
//uint8_t stx_etx_calculate_checksum(msg_t data);

/*
 *
 */
void stx_etx_send(msg_t dt)
{
	uint8_t buf[16] = {0};
	uint8_t i = 0, ii = 0;

	buf[i++] = VALUE_SYN;
	buf[i++] = VALUE_SYN;
	buf[i++] = VALUE_STX;
	buf[i++] = dt.byte_number;
	buf[i++] = dt.command;
	for(ii=0;ii<dt.byte_number;ii++)
	{
		buf[i++] = dt.data[ii];
	}
	buf[i++] = stx_etx_calculate_checksum(dt);
	buf[i++] = VALUE_ETX;

	if( serial_write(buf,i) == OK)
	{
		printf("\n\r Dados transmitido com sucesso. ");
	}
	else
	{
		printf("\n\r Falha em transmitir dados. ");
	}
}

/**
 * @brief
 * @note: https://blog.datek.com.br/2019/10/ccomo-calcular-checksum/
 */
uint8_t stx_etx_calculate_checksum(msg_t data)
{
	uint8_t checksum = 0;
	uint8_t i = 0;

	checksum ^= data.byte_number;
	checksum ^= data.command;
	for(i=0;i<data.byte_number;i++)
	{
		checksum ^= data.data[i];
	}

	return (0xFF - checksum);
}

 

/*
 * stx_etx.h
 *
 *  Created on: 8 de jan de 2020
 *      Author: Evandro Teixeira
 */

#ifndef STX_ETX_H_
#define STX_ETX_H_

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include "serial.h"

#define NUMBER_OF_ITEMS		4
#define VALUE_SYN			(uint8_t)0x16
#define VALUE_STX			(uint8_t)0x02
#define VALUE_ETX			(uint8_t)0x03

typedef struct
{
	uint8_t byte_number;
	uint8_t command;
	uint8_t data[NUMBER_OF_ITEMS];
}msg_t;

void stx_etx_send(msg_t dt);
uint8_t stx_etx_calculate_checksum(msg_t data);

#endif /* STX_ETX_H_ */

 

O algoritmo desenvolvido para Slave é uma aplicação embarcada dedicada a microcontroladores. A implementação conta com a utilização do FreeRTOS, com isso o processo foi dividido em duas Task (tarefas). 

A primeira Task é “STX ETX RX” que tem como responsabilidade processar os dados proveniente da interrupção da UART. Ela reconstrói a mensagem e checa se o conteúdo está íntegro, uma vez que a mensagem é válida esse encaminhada para as camadas da aplicação.

A segunda Task é “STX ETX TX” que tem como atribuição preparar e formata a mensagem a ser enviada ao Master. 

A seguir temos o código fonte do Slave onde contém o algoritmo que constrói e processa a mensagem, o restante do código fonte do Slave pode ser consultado no Github.

/*
 * stx_etx.c
 *
 *  Created on: 27/12/2019
 *      Author: evandro
 */

#include "../Inc/stx_etx.h"

/**
 *
 */
static data_msg_t data = {0};
QueueHandle_t queue_data_rx;
QueueHandle_t queue_msg_out;
QueueHandle_t queue_msg_in;

/**
 *
 */
void stx_etx_task_rx(void *pvParameters);
void stx_etx_task_tx(void *pvParameters);
uint8_t stx_etx_calculate_checksum(data_msg_t data);
status_interpreter_t stx_etx_interpreter(data_rx_t data_in/*, data_msg_t *data_out*/);

/**
 *
 */
void stx_etx_init(void)
{
	/* Creat Queue Data RX UART */
	queue_data_rx = xQueueCreate(NUMBER_OF_ITEMS,sizeof(data_rx_t));

	/* Creat Queue MSG In */
	queue_msg_out = xQueueCreate(NUMBER_OF_ITEMS,sizeof(data_msg_t));

	/* Creat Queue MSG Out */
	queue_msg_in = xQueueCreate(NUMBER_OF_ITEMS,sizeof(data_msg_t));

	/* Creat Task Rx */
	if(xTaskCreate(stx_etx_task_rx,"TaskRX",configMINIMAL_STACK_SIZE * 2,NULL,(configMAX_PRIORITIES-3),NULL) != pdPASS)
	{
		/* Fail in creat task */
	}

	/* Creat Task Tx */
	if(xTaskCreate(stx_etx_task_tx,"TaskTX",configMINIMAL_STACK_SIZE,NULL,(configMAX_PRIORITIES-3),NULL) != pdPASS)
	{
		/* Fail in creat task */
	}
}

/**
 *
 */
void stx_etx_task_rx(void *pvParameters)
{
	data_rx_t data_rx;

	while(1)
	{
		if(xQueueReceive(queue_data_rx,&data_rx,(TickType_t)portMAX_DELAY) == pdTRUE)
		{
			switch( stx_etx_interpreter(data_rx/*,&data_tx*/) )
			{
			    case FAULT_START_BYTE:
			    case FAULT_TIMEOUT:
			    case FAULT_INCORRECT_CRC:
			    case FAULT_STOP_BYTE:
			    case FAULT_INTERNAL_ERROR:
			    case FAULT_NO_COMMAND:
			    	/* Notify Master of Communication Failure */
			    break;
				case MSG_COMPLETED:
					/* Send Data of App*/
					if(xQueueSend(queue_msg_in,&data,(TickType_t)portMAX_DELAY) != pdPASS)
					{
						/* Failed to post the message  */
					}
				break;
				case IDLE:
				case PROCESSING:
				default:
				break;
			}
		}
	}
}

/**
 *
 */
void stx_etx_task_tx(void *pvParameters)
{
	data_msg_t data_tx = {0};
	uint8_t data[16] = {0};
	uint8_t index = 0;
	uint8_t i = 0;

	while(1)
	{
		if(xQueueReceive(queue_msg_out,&data_tx,(TickType_t)portMAX_DELAY) == pdTRUE)
		{
			/* Prepares data to be transmitted by UART */
			index = 0;
			data[index++] = VALUE_SYN;
			data[index++] = VALUE_SYN;
			data[index++] = VALUE_STX;
			data[index++] = data_tx.byte_number;
			data[index++] = data_tx.command;
			for(i=0;i<data_tx.byte_number;i++)
			{
				data[index++] = data_tx.data[i];
			}
			data[index++] = stx_etx_calculate_checksum(data_tx);
			data[index++] = VALUE_ETX;

			/* Transmit data by UART */
			MX_USART1_UART_Transmit(data,index);
		}
	}
}

/**
 *
 */
void stx_etx_queue_get_data(uint8_t data)
{
	data_rx_t data_rx;
	data_rx.data = data;
	data_rx.time = HAL_GetTick();
	xQueueSendFromISR(queue_data_rx,&data_rx,pdFALSE );
}

/**
 *
 */
bool stx_etx_queue_receive(data_msg_t *data_out, uint32_t tick)
{
	bool ret = false;
	static data_msg_t data = {0};

	if(xQueueReceive(queue_msg_in,&data,(TickType_t)tick) == pdTRUE)
	{
		*data_out = data;
		ret = true;
	}
	return ret;
}

/**
 *
 */
bool stx_etx_queue_send(data_msg_t data,uint32_t tick)
{
	bool ret = false;

	if(xQueueSend(queue_msg_out,&data,(TickType_t)tick) == pdPASS)
	{
		ret = true;
	}

	return ret;
}

/**
 * @brief
 * @note: https://blog.datek.com.br/2019/10/ccomo-calcular-checksum/
 */
uint8_t stx_etx_calculate_checksum(data_msg_t data)
{
	uint8_t checksum = 0;
	uint8_t i = 0;

	checksum ^= data.byte_number;
	checksum ^= data.command;
	for(i=0;i<data.byte_number;i++)
	{
		checksum ^= data.data[i];
	}

	return (0xFF - checksum);
}

/**
 *
 */
status_interpreter_t stx_etx_interpreter(data_rx_t data_in /*,data_msg_t *data_out*/)
{
	static state_data_t state_data = BYTE_NUMBER;
	static state_interpreter_t state_interpreter = SYN1;
	static data_rx_t data_old = {0};
	static uint8_t index_data = 0;
	status_interpreter_t ret = IDLE;

	/* Check the time delta between bytes */
	if((data_in.time - data_old.time) > TIMEOUT)
	{
		/* returns to initial state */
		state_interpreter = SYN1;
	}

	switch(state_interpreter)
	{
		case SYN1: /* Start Byte 0 */
			/* Check byte value SYN */
			if(data_in.data == VALUE_SYN)
			{
				state_interpreter = SYN2;
				ret = PROCESSING;
			}
			else
			{
				/* signals failure and returns to initial state */
				ret = FAULT_START_BYTE;
			}
		break;
		case SYN2: /* Start Byte 1 */
			/* Check byte value SYN */
			if(data_in.data == VALUE_SYN)
			{
				state_interpreter = STX;
				ret = PROCESSING;
			}
			else
			{
				/* signals failure and returns to initial state */
				state_interpreter = SYN1;
				ret = FAULT_START_BYTE;
			}
		break;
		case STX: /* Start Byte 2 */
			/* Check byte value STX */
			if(data_in.data == VALUE_STX)
			{
				state_interpreter = DATA;
				state_data = BYTE_NUMBER;
				ret = PROCESSING;
			}
			else
			{
				/* signals failure and returns to initial state */
				state_interpreter = SYN1;
				ret = FAULT_START_BYTE;
			}
		break;
		case DATA: /* Message assembler */
			ret = PROCESSING;

			switch(state_data)
			{
				case BYTE_NUMBER:
					state_data = COMMAND;
					data.byte_number = data_in.data;
				break;
				case COMMAND:
					if(data.byte_number == 0)
					{
						state_data = BYTE_NUMBER;
						state_interpreter = CHK_CRC;
					}
					else
					{
						state_data = DATA_ASSEMBLER;
					}
					data.command = data_in.data;
					index_data=0;
				break;
				case DATA_ASSEMBLER:
					//msg.data.data[index_data++] = data_in.data;
					data.data[index_data++] = data_in.data;
					if(index_data >= data.byte_number)
					{
						state_data = BYTE_NUMBER;
						state_interpreter = CHK_CRC;
					}
				break;
				case MAX_STATE_DATA:
				default:
					/* signals failure and returns to initial state */
					state_interpreter = SYN1;
					state_data = BYTE_NUMBER;
					ret = FAULT_INTERNAL_ERROR;
				break;
			}
		break;
		case CHK_CRC:
			/* computes the received message's CRC value
			 * and compares the transmitted CRC value */
			if(data_in.data == stx_etx_calculate_checksum(data))
			{
				state_interpreter = ETX;
			}
			else
			{
				/* signals failure and returns to initial state */
				state_interpreter = SYN1;
				ret = FAULT_INCORRECT_CRC;
			}
		break;
		case ETX: /* Stop Byte  */
			if(data_in.data == VALUE_ETX)
			{
				state_interpreter = SYN1;
				ret = MSG_COMPLETED;
			}
			else
			{
				/* signals failure and returns to initial state */
				state_interpreter = SYN1;
				ret = FAULT_STOP_BYTE;
			}
		break;
		case MAX_STATE_INTERPRETER:
		default:
			/* signals failure and returns to initial state */
			state_interpreter = SYN1;
			ret = FAULT_INTERNAL_ERROR;
		break;
	}

	/* update variable that stores last data */
	data_old = data_in;

	return ret;
}

 

/*
 * stx_etx.h
 *
 *  Created on: 27/12/2019
 *      Author: Evandro Teixeira
 */

#ifndef PROTOCOLOSTXETX_INC_STX_ETX_H_
#define PROTOCOLOSTXETX_INC_STX_ETX_H_

#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"
#include "usart.h"
#include "crc.h"
#include <stdbool.h>

#define NUMBER_OF_ITEMS		4
#define VALUE_SYN			(uint8_t)0x16
#define VALUE_STX			(uint8_t)0x02
#define VALUE_ETX			(uint8_t)0x03
#define TIMEOUT				(uint32_t)100000

typedef enum
{
	SYN1 = 0,
	SYN2,
	STX,
	DATA,
	CHK_CRC,
	ETX,
	MAX_STATE_INTERPRETER
}state_interpreter_t;

typedef enum
{
	IDLE = 0,
	PROCESSING,
	MSG_COMPLETED,
	FAULT_START_BYTE,
	FAULT_TIMEOUT,
	FAULT_INCORRECT_CRC,
	FAULT_STOP_BYTE,
	FAULT_NO_COMMAND,
	FAULT_INTERNAL_ERROR
}status_interpreter_t;

typedef enum
{
	BYTE_NUMBER = 0,
	COMMAND,
	DATA_ASSEMBLER,
	MAX_STATE_DATA
}state_data_t;

typedef struct
{
	uint8_t data;
	uint32_t time;
}data_rx_t;

typedef struct
{
	//uint8_t config;
	uint8_t byte_number;
	uint8_t command;
	uint8_t data[4];
}data_msg_t;

/*typedef struct
{
	uint8_t syn1;
	uint8_t syn2;
	uint8_t stx;
	data_msg_t data;
	uint8_t crc;
	uint8_t etx;
}msg_t;*/

void stx_etx_init(void);
void stx_etx_queue_get_data(uint8_t data);
//uint8_t stx_etx_calculate_checksum(data_msg_t data, data_msg_t *data_out);
bool stx_etx_queue_receive(data_msg_t *data_out,uint32_t tick);
bool stx_etx_queue_send(data_msg_t data,uint32_t tick);

#endif /* PROTOCOLOSTXETX_INC_STX_ETX_H_ */

 

Demonstração


A aplicação de demonstração para o protocolo de comunicação STX-ETX é bem simples, consiste em:

  • Master preparar e envia comandos para o Slave.
  • O Slave por sua vez recebe os comandos, processa e toma as ações de acordo com comandos recebidos. Segue a tabelas com os comandos:

 

Master

MSG

Command

Data

Set LED Green

1

Off: 0 | On: 1

Set LED Blue

2

Off: 0 | On: 1

Get Status Button

3

NA

Slave

MSG

Status

Data

Set LED Green

Ok: 1 | Fail: 0

NA | Cod. Error

Set LED Blue

Ok: 1 | Fail: 0

NA | Cod. Error

Get Status Button

Ok: 1 | Fail: 0

Value Button | Cod. Error

 

Como dito anteriormente a aplicação master é desenvolvida para desktop. Ela interage com o usuário para montar as mensagens a ser enviada para o Slave. A seguir temos algumas imagens da interface do usuário com alguns comandos enviados.

 

Comando para acionar o LED Azul
Comando para acionar o LED Azul

 

O Protocolo de Comunicação STX-ETX
Comando para acionar o LED verde

 

O Protocolo de Comunicação STX-ETX
Comando para ler o status do User Button

 

A aplicação desenvolvida para o Slave, é um firmware para STM32F0DISCOVERY, trata-se de kit de desenvolvimento para o microcontrolador STM32F0 que por sua vez é baseado arquitetura ARM Cortex-M0. Os comandos propostos para a aplicação limitam-se em acionar os LED’s presente na STM32F0DISCOVERY e ler o status do USER Button. A seguir temos figura que ilustra a arquitetura do software presente do Slave.

 

Diagrama da aplicação de demonstração
Diagrama da aplicação de demonstração

 

O algoritmo do Slave consiste em ler os dados recebidos pelo barramento serial (UART), reconstruir a mensagem e tomar as ações de acordo com as mensagens recebidas.

 

/*
 * app.c
 *
 *  Created on: 03/01/2020
 *      Author: Evandro Teixeira
 */

#include "../Inc/app.h"

/**
 *
 */
void app_task(void *pvParameters);
void app_led_green(bool st);
void app_led_blue(bool st);
bool app_status_button_get(void);

/**
 *
 */
static bool status_button = false;
extern QueueHandle_t queue_msg_in;
/**
 *
 */
void app_init(void)
{
	/* Creat Task App */
	if(xTaskCreate(app_task,"App",configMINIMAL_STACK_SIZE,NULL,(configMAX_PRIORITIES-4),NULL) != pdPASS)
	{
		/* Fail in creat task */
	}
}

/**
 *
 */
void app_task(void *pvParameters)
{
	static data_msg_t data_in = {0};
	static data_msg_t data_out = {0};

	app_led_green(0 /* Off */);
	app_led_blue(0 /* Off */);

	while(1)
	{
		if(stx_etx_queue_receive(&data_in,portMAX_DELAY) == true)
		//if(xQueueReceive(queue_msg_in,&data_in,(TickType_t)portMAX_DELAY) == pdTRUE)
		{
			/* State machine with the commands */
			switch(data_in.command)
			{
				case APP_LED_GREEN:
					/* Set LED Green */
					app_led_green((bool)data_in.data[0]);
					/* Prepare data to be transmitted */
					data_out.byte_number = 0;
					data_out.command = 1; // Status Ok
				break;
				case APP_LED_BLUE:
					/* Set LED Blue */
					app_led_blue((bool)data_in.data[0]);
					/* Prepare data to be transmitted */
					data_out.byte_number = 0;
					data_out.command = 1; // Status Ok
				break;
				case APP_STATUS_BUTTON:
					/* Get Status Button */
					data_out.byte_number = 1;
					data_out.command = 1; // Status Ok
					data_out.data[0] = (uint8_t)(app_status_button_get());
				break;
				case APP_NO_COMMAND:
				default:
					/* Signals failure */
					data_out.byte_number = 1;
					data_out.command = 0; 	// Status Faul
					data_out.data[0] = 255;	// Cod. Error
				break;
			}

			/* Send MSG */
			stx_etx_queue_send(data_out,portMAX_DELAY);
		}
	}
}

/**
 *
 */
void app_led_green(bool st)
{
	HAL_GPIO_WritePin(GPIOC,LD3_Pin,(GPIO_PinState)st);
}

/**
 *
 */
void app_led_blue(bool st)
{
	HAL_GPIO_WritePin(GPIOC,LD4_Pin,(GPIO_PinState)st);
}

/**
 *
 */
void app_status_button_set(bool data)
{
	status_button = data;
}

/**
 *
 */
bool app_status_button_get(void)
{
	status_button = HAL_GPIO_ReadPin(USER_BUTTON_GPIO_Port,USER_BUTTON_Pin);
	return status_button;
}

 

/*
 * app.h
 *
 *  Created on: 03/01/2020
 *      Author: Evandro Teixeira
 */

#ifndef APP_INC_APP_H_
#define APP_INC_APP_H_

#include "FreeRTOS.h"
#include "task.h"
#include "main.h"
#include "cmsis_os.h"
#include "../Inc/stx_etx.h"
#include <stdbool.h>

typedef enum
{
	APP_LED_GREEN = 1,
	APP_LED_BLUE,
	APP_STATUS_BUTTON,
	APP_NO_COMMAND
}app_command_t;

void app_init(void);
void app_status_button_set(bool data);

#endif /* APP_INC_APP_H_ */

 

Conclusão

 

O protocolo de comunicação STX-ETX é uma boa alternativa para projetos mais simples que necessitam de uma comunicação ponto a ponto entres Master e Slave. É um padrão de fácil implementação.

O que você achou? Você trabalha ou já trabalhou com o protocolo de comunicação STX-ETX? Deixe o seu comentário a abaixo.

 

Saiba Mais

FreeMODBUS – Apresentação & Port

Protocolo Modbus: Fundamentos e Aplicações

Criando seu próprio shell para sistemas embarcados

 

 

Referência

 https://github.com/evandro-teixeira/protocolo_stx_etx

 

Sem licença
Comentários:
Notificações
Notificar
8 Comentários
recentes
antigos mais votados
Inline Feedbacks
View all comments
Edemilso Ilha
Edemilso Ilha
16/04/2023 18:28

Excelente artigo e ideias. Já implementei comunicação serial assíncrona com 1 Master e diversos Slaves e as ideias apresentadas aqui servirão de ajuda nos próximos projetos.

Evandro Teixeira
Evandro Teixeira
Reply to  Edemilso Ilha
17/04/2023 13:09

Olá Edemilso, fico feliz em saber que você gostou do artigo.
Recentemente implementei também implementei com um Master e vários Slaves, sobre o protocolo RS485, funcionou muito bem. Tive que adicionar mais camada de software no Master para o gerenciamento dos dispositivos. Ficou bem legal! 😉

Bruno de Lima
Bruno de Lima
02/06/2021 17:48

Olá Evandro Teixeira, gostei muito do seu trabalho, parabéns. Eu estou trabalhando em um projeto e preciso enviar alguns dados através do protocolo de comunicação STX ETX, poderia me dar alguma dica?

Evandro Teixeira
Evandro Teixeira
Reply to  Bruno de Lima
04/06/2021 08:46

Bruno de Lima fico feliz que você gostou do artigo.

É claro que posso te ajudar.

Francis David
Francis David
05/08/2020 23:32

Facil e digo que até intuitivo, comecei a desenvolver um protocolo para uma arquitetura master/slave não tinha conhecimento dessa, entretanto, o que fiz é muito parecido com o que foi descrito:
STX | ID | VALUE | CRC | ETX
Obrigado por compartilhar.

Evandro Teixeira
Evandro Teixeira
Reply to  Francis David
06/08/2020 12:55

Francis David fico feliz em saber que você gostou do artigo.
Conforme o Jeu Tonete apontou em seu comentário, existe alguns pontos a serem melhorados no algoritmo.

Jeu Tonete
Jeu Tonete
15/05/2020 09:00

Ótimo artigo, parabéns. Utilizo um protocolo semelhante a esse há algum tempo. O que difere é o que não utilizo o STX-ETX no header e final do frame. Funciona muito bem em comunicações digitais assíncronas, inclusive uso na comunicação entre módulo Bluetooth e microcontrolador. Vale ressaltar duas coisas: 1- O tamanho máximo de dados que pode ser trafegado nesse protocolo é 255 (máximo valor do MSG byte number) e precisa ser filtrado quando recebido para não estourar os vetores de recepção. 2- É válido comentar sobre o timeout na recepção que você implementou. Sem ele é muito fácil perder o… Leia mais »

Home » Comunicação » Protocolo de Comunicação STX-ETX

EM DESTAQUE

WEBINARS

VEJA TAMBÉM

JUNTE-SE HOJE À COMUNIDADE EMBARCADOS

Talvez você goste:
Nenhum resultado encontrado.