agrobot_base/Firmware/Modulos/ModbusService.h

237 lines
7.3 KiB
C++

#include <Arduino.h>
#include <HardwareSerial.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/queue.h>
#include <freertos/semphr.h>
#ifndef ModbusService
#define ModbusService
// Fila para armazenar mensagens Modbus
QueueHandle_t modbusQueue;
TaskHandle_t modbusTaskHandle = NULL;
// Definir prioridades
#define PRIORITY_HIGH 1
#define PRIORITY_LOW 0
HardwareSerial SerialDriver(1);
long ModbusBaudRate = 9600;
int RXD1 = 16;
int TXD1 = 17;
int DE_RE_PIN = 4;
bool Debug = false;
class ModbusGetResponse {
public:
byte _address;
byte _command_code;
byte _num_registers;
double _value;
bool _success = false;
ModbusGetResponse() : _address(0), _command_code(0), _num_registers(0), _value(0.0) {}
};
// Estrutura para armazenar dados da mensagem Modbus
struct ModbusMessage {
uint8_t slaveAddress;
uint8_t functionCode;
uint16_t registerAddress;
int value;
uint8_t* response;
uint8_t* length;
SemaphoreHandle_t responseSemaphore;
uint8_t priority;
bool waitCallback;
};
// Função para calcular CRC16
uint16_t calculateCRC(const uint8_t* data, uint8_t length) {
uint16_t crc = 0xFFFF;
for (uint8_t i = 0; i < length; i++) {
crc ^= data[i];
for (uint8_t j = 0; j < 8; j++) {
if (crc & 0x0001) {
crc >>= 1;
crc ^= 0xA001;
} else {
crc >>= 1;
}
}
}
return crc;
}
void processModbusMessage(ModbusMessage& message) {
uint8_t command[8];
command[0] = message.slaveAddress;
command[1] = message.functionCode;
command[2] = (message.registerAddress >> 8) & 0xFF; // Big endian
command[3] = message.registerAddress & 0xFF;
// Converter o valor inteiro para o formato adequado
command[4] = (message.value >> 8) & 0xFF; // Big endian
command[5] = message.value & 0xFF;
uint16_t crc = calculateCRC(command, 6);
command[6] = crc & 0xFF;
command[7] = (crc >> 8) & 0xFF;
// Alternar para modo de transmissão
digitalWrite(DE_RE_PIN, HIGH);
vTaskDelay(pdMS_TO_TICKS(1)); // Pequeno atraso para garantir que o modo de transmissão esteja ativo
SerialDriver.write(command, 8);
if (Debug) {
// Imprimir o comando em formato hexadecimal
Serial.print("Comando enviado: ");
for (int i = 0; i < 8; i++) {
if (command[i] < 0x10) {
Serial.print("0"); // Adiciona um zero à esquerda para bytes menores que 0x10
}
Serial.print(command[i], HEX);
Serial.print(" ");
}
Serial.println();
}
// Alternar para modo de recepção
SerialDriver.flush(); // Garantir que todos os dados foram enviados
digitalWrite(DE_RE_PIN, LOW);
vTaskDelay(pdMS_TO_TICKS(1)); // Pequeno atraso para garantir que o modo de recepção esteja ativo
*message.length = 0;
if (message.waitCallback) {
// Aguardar a resposta
vTaskDelay(pdMS_TO_TICKS(100)); // Ajuste o tempo de delay conforme necessário
// Ler a resposta
if (SerialDriver.available()) {
*message.length = SerialDriver.readBytes(message.response, SerialDriver.available());
if (Debug) {
// Imprimir a resposta em formato hexadecimal
Serial.print("Resposta recebida: ");
for (int i = 0; i < *message.length; i++) {
if (message.response[i] < 0x10) {
Serial.print("0"); // Adiciona um zero à esquerda para bytes menores que 0x10
}
Serial.print(message.response[i], HEX);
Serial.print(" ");
}
Serial.println();
}
}
}
else {
vTaskDelay(pdMS_TO_TICKS(50));
}
// Sinalizar que a resposta está disponível
xSemaphoreGive(message.responseSemaphore);
}
void processModbusQueue(void* pvParameters) {
ModbusMessage message;
while (true) {
// Aguardar até que uma mensagem esteja disponível na fila
if (xQueueReceive(modbusQueue, &message, portMAX_DELAY) == pdPASS) {
// Processar mensagem de alta prioridade imediatamente
if (message.priority == PRIORITY_HIGH) {
// Processar mensagem de alta prioridade
processModbusMessage(message);
} else {
// Verificar se há mensagens de alta prioridade na fila
ModbusMessage highPriorityMessage;
if (xQueuePeek(modbusQueue, &highPriorityMessage, 0) == pdPASS && highPriorityMessage.priority == PRIORITY_HIGH) {
// Recolocar a mensagem de baixa prioridade na fila
xQueueSendToBack(modbusQueue, &message, 0);
} else {
// Processar mensagem de baixa prioridade
processModbusMessage(message);
}
}
}
}
}
// Função para inicializar o Modbus
void InicializarModbus(bool iniciar, int _rx, int _tx, int _md, long _baudRate = 9600, bool _debug = false) {
// Se a comunicação serial já estiver inicializada, encerre-a
if (SerialDriver) {
SerialDriver.end();
}
// Se a fila já estiver criada, delete-a
if (modbusQueue != NULL) {
vQueueDelete(modbusQueue);
modbusQueue = NULL;
}
// Se a tarefa já estiver criada, delete-a
if (modbusTaskHandle != NULL) {
vTaskDelete(modbusTaskHandle);
modbusTaskHandle = NULL;
}
if (!iniciar) {
return;
}
// Inicializar os novos pinos
RXD1 = _rx;
TXD1 = _tx;
DE_RE_PIN = _md;
ModbusBaudRate = _baudRate;
// Inicializar a comunicação serial
SerialDriver.begin(ModbusBaudRate, SERIAL_8N2, RXD1, TXD1);
Serial.println("Modbus Inicializado");
// Configurar o pino DE/RE
pinMode(DE_RE_PIN, OUTPUT);
digitalWrite(DE_RE_PIN, LOW); // Inicialmente em modo de recepção
Debug = _debug;
// Criar a fila de mensagens Modbus
modbusQueue = xQueueCreate(100, sizeof(ModbusMessage));
if (modbusQueue == NULL) {
Serial.println("Erro ao criar a fila de mensagens Modbus");
}
// Criar a tarefa de processamento da fila
xTaskCreate(processModbusQueue, "ProcessModbusQueue", 4096, NULL, 1, &modbusTaskHandle);
}
// Função para construir o comando Modbus
void sendModbusCommand(uint8_t slaveAddress, uint8_t functionCode, uint16_t registerAddress, int value, uint8_t* response, uint8_t& length, bool waitCallback) {
ModbusMessage message;
message.slaveAddress = slaveAddress;
message.functionCode = functionCode;
message.registerAddress = registerAddress;
message.value = value;
message.response = response;
message.length = &length;
message.priority = functionCode == 0x06 ? PRIORITY_HIGH : PRIORITY_LOW;
message.waitCallback = waitCallback;
message.responseSemaphore = xSemaphoreCreateBinary();
// Adicionar a mensagem à fila
if (xQueueSend(modbusQueue, &message, portMAX_DELAY) != pdPASS) {
Serial.println("Erro ao adicionar mensagem à fila");
}
// Esperar até que a resposta esteja disponível
xSemaphoreTake(message.responseSemaphore, portMAX_DELAY);
vSemaphoreDelete(message.responseSemaphore);
}
#endif