First version of RS485 + MODBUS Driver
This commit is contained in:
@@ -3,7 +3,6 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <endian.h>
|
#include <endian.h>
|
||||||
|
|
||||||
HardwareSerial lidarSerial(1); // Using serial port 1
|
|
||||||
uint8_t data[][8] = {
|
uint8_t data[][8] = {
|
||||||
// ESP32-S3-POE-ETH-8DI-8RO Control Command (RS485 receiving data)
|
// ESP32-S3-POE-ETH-8DI-8RO Control Command (RS485 receiving data)
|
||||||
{0x06, 0x05, 0x00, 0x01, 0x55, 0x00, 0xA2, 0xED}, // ESP32-S3-POE-ETH-8DI-8RO CH1 Toggle
|
{0x06, 0x05, 0x00, 0x01, 0x55, 0x00, 0xA2, 0xED}, // ESP32-S3-POE-ETH-8DI-8RO CH1 Toggle
|
||||||
@@ -30,151 +29,6 @@ uint8_t Send_Data[][8] = {
|
|||||||
{0x01, 0x05, 0x00, 0xFF, 0xFF, 0xFF, 0xFC, 0x4A}, // Modbus RTU Relay ALL ON
|
{0x01, 0x05, 0x00, 0xFF, 0xFF, 0xFF, 0xFC, 0x4A}, // Modbus RTU Relay ALL ON
|
||||||
{0x01, 0x05, 0x00, 0xFF, 0x00, 0x00, 0xFD, 0xFA}, // Modbus RTU Relay ALL OFF
|
{0x01, 0x05, 0x00, 0xFF, 0x00, 0x00, 0xFD, 0xFA}, // Modbus RTU Relay ALL OFF
|
||||||
};
|
};
|
||||||
uint8_t buf[20] = {0}; // Data storage area
|
|
||||||
int numRows = sizeof(data) / sizeof(data[0]);
|
|
||||||
|
|
||||||
void SetData(uint8_t *data, size_t length)
|
|
||||||
{
|
|
||||||
lidarSerial.write(data, length); // Send data from the RS485
|
|
||||||
}
|
|
||||||
void ReadData(uint8_t *buf, uint8_t length)
|
|
||||||
{
|
|
||||||
uint8_t Receive_Flag = 0;
|
|
||||||
Receive_Flag = lidarSerial.available();
|
|
||||||
if (Receive_Flag >= length)
|
|
||||||
{
|
|
||||||
lidarSerial.readBytes(buf, length);
|
|
||||||
char printBuf[length * 3 + 1];
|
|
||||||
sprintf(printBuf, "Received data: ");
|
|
||||||
for (int i = 0; i < length; i++)
|
|
||||||
{
|
|
||||||
sprintf(printBuf + strlen(printBuf), "%02X ", buf[i]);
|
|
||||||
}
|
|
||||||
printf(printBuf);
|
|
||||||
/*************************
|
|
||||||
Add a receiving data handler
|
|
||||||
*************************/
|
|
||||||
Receive_Flag = 0;
|
|
||||||
memset(buf, 0, sizeof(buf));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void RS485_Analysis(uint8_t *buf)
|
|
||||||
{
|
|
||||||
switch (buf[1])
|
|
||||||
{
|
|
||||||
case Extension_CH1:
|
|
||||||
SetData(Send_Data[0], sizeof(Send_Data[0]));
|
|
||||||
printf("|*** Toggle expansion channel 1 ***|\r\n");
|
|
||||||
break;
|
|
||||||
case Extension_CH2:
|
|
||||||
SetData(Send_Data[1], sizeof(Send_Data[1]));
|
|
||||||
printf("|*** Toggle expansion channel 2 ***|\r\n");
|
|
||||||
break;
|
|
||||||
case Extension_CH3:
|
|
||||||
SetData(Send_Data[2], sizeof(Send_Data[2]));
|
|
||||||
printf("|*** Toggle expansion channel 3 ***|\r\n");
|
|
||||||
break;
|
|
||||||
case Extension_CH4:
|
|
||||||
SetData(Send_Data[3], sizeof(Send_Data[3]));
|
|
||||||
printf("|*** Toggle expansion channel 4 ***|\r\n");
|
|
||||||
break;
|
|
||||||
case Extension_CH5:
|
|
||||||
SetData(Send_Data[4], sizeof(Send_Data[4]));
|
|
||||||
printf("|*** Toggle expansion channel 5 ***|\r\n");
|
|
||||||
break;
|
|
||||||
case Extension_CH6:
|
|
||||||
SetData(Send_Data[5], sizeof(Send_Data[5]));
|
|
||||||
printf("|*** Toggle expansion channel 6 ***|\r\n");
|
|
||||||
break;
|
|
||||||
case Extension_CH7:
|
|
||||||
SetData(Send_Data[6], sizeof(Send_Data[6]));
|
|
||||||
printf("|*** Toggle expansion channel 7 ***|\r\n");
|
|
||||||
break;
|
|
||||||
case Extension_CH8:
|
|
||||||
SetData(Send_Data[7], sizeof(Send_Data[7]));
|
|
||||||
printf("|*** Toggle expansion channel 8 ***|\r\n");
|
|
||||||
break;
|
|
||||||
case Extension_ALL_ON:
|
|
||||||
SetData(Send_Data[8], sizeof(Send_Data[8]));
|
|
||||||
printf("|*** Enable all extension channels ***|\r\n");
|
|
||||||
break;
|
|
||||||
case Extension_ALL_OFF:
|
|
||||||
SetData(Send_Data[9], sizeof(Send_Data[9]));
|
|
||||||
printf("|*** Close all expansion channels ***|\r\n");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
printf("Note : Non-control external device instructions !\r\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
uint32_t Baudrate = 0;
|
|
||||||
double transmission_time = 0;
|
|
||||||
double RS485_cmd_Time = 0;
|
|
||||||
void RS485_Init() // Initializing serial port
|
|
||||||
{
|
|
||||||
Baudrate = 9600; // Set the baud rate of the serial port
|
|
||||||
lidarSerial.begin(Baudrate, SERIAL_8N1, RXD1, TXD1); // Initializing serial port
|
|
||||||
transmission_time = 10.0 / Baudrate * 1000;
|
|
||||||
RS485_cmd_Time = transmission_time * 8; // 8:data length
|
|
||||||
xTaskCreatePinnedToCore(
|
|
||||||
RS485Task,
|
|
||||||
"RS485Task",
|
|
||||||
4096,
|
|
||||||
NULL,
|
|
||||||
3,
|
|
||||||
NULL,
|
|
||||||
0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RS485Task(void *parameter)
|
|
||||||
{
|
|
||||||
while (1)
|
|
||||||
{
|
|
||||||
RS485_Loop();
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(50));
|
|
||||||
}
|
|
||||||
vTaskDelete(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RS485_Loop()
|
|
||||||
{
|
|
||||||
uint8_t Receive_Flag = 0; // Receiving mark
|
|
||||||
Receive_Flag = lidarSerial.available();
|
|
||||||
|
|
||||||
if (Receive_Flag > 0)
|
|
||||||
{
|
|
||||||
if (RS485_cmd_Time > 1) // Time greater than 1 millisecond
|
|
||||||
delay((uint16_t)RS485_cmd_Time);
|
|
||||||
else // Time is less than 1 millisecond
|
|
||||||
delay(1);
|
|
||||||
Receive_Flag = lidarSerial.available();
|
|
||||||
lidarSerial.readBytes(buf, Receive_Flag); // The Receive_Flag length is read
|
|
||||||
if (Receive_Flag == 8)
|
|
||||||
{
|
|
||||||
uint8_t i = 0;
|
|
||||||
for (i = 0; i < numRows; i++)
|
|
||||||
{
|
|
||||||
bool result = std::equal(std::begin(buf), std::begin(buf) + 8, std::begin(data[i])); // Compare two arrays
|
|
||||||
if (result)
|
|
||||||
{
|
|
||||||
if (i < numRows - 1)
|
|
||||||
buf[0] = i + 1 + 48;
|
|
||||||
else if (i == numRows - 1)
|
|
||||||
buf[0] = 48;
|
|
||||||
Relay_Analysis(buf, RS485_Mode);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (i > numRows - 1)
|
|
||||||
printf("Note : Non-instruction data was received - RS485 !\r\n");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf("Note : Non-instruction data was received .Number of bytes: %d - RS485 !\r\n", Receive_Flag);
|
|
||||||
}
|
|
||||||
Receive_Flag = 0;
|
|
||||||
memset(buf, 0, sizeof(buf));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace drivers
|
namespace drivers
|
||||||
{
|
{
|
||||||
@@ -230,6 +84,7 @@ namespace drivers
|
|||||||
const bool MODBUS::readCoils(const uint8_t device, const uint16_t reg, const uint16_t num, std::vector<bool> &coils)
|
const bool MODBUS::readCoils(const uint8_t device, const uint16_t reg, const uint16_t num, std::vector<bool> &coils)
|
||||||
{
|
{
|
||||||
constexpr uint8_t func = 0x01;
|
constexpr uint8_t func = 0x01;
|
||||||
|
log_d("Read coils: dev[%02x], reg[%04x], num[%d]", device, reg, num);
|
||||||
readBinary(func, device, reg, num, coils);
|
readBinary(func, device, reg, num, coils);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,22 +92,75 @@ namespace drivers
|
|||||||
const bool MODBUS::readInputs(const uint8_t device, const uint16_t reg, const uint8_t num, std::vector<bool> &inputs)
|
const bool MODBUS::readInputs(const uint8_t device, const uint16_t reg, const uint8_t num, std::vector<bool> &inputs)
|
||||||
{
|
{
|
||||||
constexpr uint8_t func = 0x01;
|
constexpr uint8_t func = 0x01;
|
||||||
|
log_d("Read multi inputs: dev[%02x], reg[%04x], num[%d]", device, reg, num);
|
||||||
readBinary(func, device, reg, num, inputs);
|
readBinary(func, device, reg, num, inputs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Func 0x03
|
||||||
|
const bool MODBUS::readHoldingRegisters(const uint8_t device, const uint16_t reg, const uint8_t num, std::vector<uint16_t> &values)
|
||||||
|
{
|
||||||
|
constexpr uint8_t func = 0x03;
|
||||||
|
log_d("Read multi holding registers: dev[%02x], reg[%04x], num[%d]", device, reg, num);
|
||||||
|
readInteger(func, device, reg, num, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Func 0x04
|
||||||
|
const bool MODBUS::readInputRegisters(const uint8_t device, const uint16_t reg, const uint8_t num, std::vector<uint16_t> &values)
|
||||||
|
{
|
||||||
|
constexpr uint8_t func = 0x04;
|
||||||
|
log_d("Read multi input registers: dev[%02x], reg[%04x], num[%d]", device, reg, num);
|
||||||
|
readInteger(func, device, reg, num, values);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Func 0x05
|
||||||
|
const bool MODBUS::writeCoil(const uint8_t device, const uint16_t coil, const bool value)
|
||||||
|
{
|
||||||
|
constexpr uint8_t func = 0x05;
|
||||||
|
log_d("Write single coil: dev[%02x], reg[%04x], val[...]", device, reg);
|
||||||
|
return writeBinary(device, func, coil, 1, {value});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Func 0x06
|
||||||
|
const bool MODBUS::writeRegister(const uint8_t device, const uint16_t reg, const uint16_t value)
|
||||||
|
{
|
||||||
|
constexpr uint8_t func = 0x06;
|
||||||
|
log_d("Write single register: dev[%02x], reg[%04x], val[%04x]", device, reg, value);
|
||||||
|
return writeInteger(device, func, reg, 1, {value});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Func 0x0F
|
||||||
|
const bool MODBUS::writeCoils(const uint8_t device, const uint16_t coils, const std::vector<bool> &values)
|
||||||
|
{
|
||||||
|
constexpr uint8_t func = 0x0F;
|
||||||
|
log_d("Write multi coils: dev[%02x], reg[%04x], val[...]", device, reg);
|
||||||
|
return writeBinary(device, func, coils, values.size(), values);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Func 0x10
|
||||||
|
const bool MODBUS::writeRegisters(const uint8_t device, const uint16_t reg, const std::vector<uint16_t> &values)
|
||||||
|
{
|
||||||
|
constexpr uint8_t func = 0x10;
|
||||||
|
log_d("Write multi registers: dev[%02x], reg[%04x], val[...]", device, reg);
|
||||||
|
return writeInteger(device, func, reg, values.size(), values);
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
/////////////////////// Utility Functions ///////////////////////
|
||||||
|
/////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
const bool MODBUS::readBinary(const uint8_t func, const uint8_t device, const uint16_t reg, const uint16_t bits, std::vector<bool> &out)
|
const bool MODBUS::readBinary(const uint8_t func, const uint8_t device, const uint16_t reg, const uint16_t bits, std::vector<bool> &out)
|
||||||
{
|
{
|
||||||
if (!write(buildRequest(device, func, reg, bits)))
|
if (!write(singleRequest(device, func, reg, bits)))
|
||||||
{
|
{
|
||||||
log_e("Failed send readCoils command");
|
log_e("Failed send readBinary command");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const uint16_t nRespDataBytes = 1 + (bits % 8); // 1 bit for every coil, if not 8 mutiple padded with zeroes
|
const uint16_t nRespDataBytes = 1 + (uint16_t)(bits / 8); // 1 bit for every coil, if not 8 mutiple padded with zeroes
|
||||||
const uint16_t expectedRespLen = (RESP_HEADER_SIZE + RESP_CRC_SIZE) + nRespDataBytes; // device + function + nbytes + data[] + crc(16b)
|
const uint16_t expectedRespLen = (RESP_HEADER_SIZE + RESP_CRC_SIZE) + nRespDataBytes; // device + function + nbytes + data[] + crc(16b)
|
||||||
std::vector<uint8_t> response;
|
std::vector<uint8_t> response;
|
||||||
if (!readN(expectedRespLen, response))
|
if (!readN(expectedRespLen, response))
|
||||||
{
|
{
|
||||||
log_e("Failed receive readCoils response");
|
log_e("Failed receive readBinary response");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -286,21 +194,11 @@ namespace drivers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Func 0x03
|
|
||||||
const bool MODBUS::readHoldingRegisters(const uint8_t device, const uint16_t reg, const uint8_t num, std::vector<uint16_t> &values)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Func 0x04
|
|
||||||
const bool MODBUS::readInputRegisters(const uint8_t device, const uint16_t reg, const uint8_t num, std::vector<uint16_t> &values)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
const bool MODBUS::readInteger(const uint8_t func, const uint8_t device, const uint16_t reg, const uint16_t num, std::vector<uint16_t> &out)
|
const bool MODBUS::readInteger(const uint8_t func, const uint8_t device, const uint16_t reg, const uint16_t num, std::vector<uint16_t> &out)
|
||||||
{
|
{
|
||||||
if (!write(buildRequest(device, func, reg, num)))
|
if (!write(singleRequest(device, func, reg, num)))
|
||||||
{
|
{
|
||||||
log_e("Failed send readCoils command");
|
log_e("Failed send readInteger command");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const uint16_t nRespDataBytes = num * sizeof(uint16_t);
|
const uint16_t nRespDataBytes = num * sizeof(uint16_t);
|
||||||
@@ -308,7 +206,7 @@ namespace drivers
|
|||||||
std::vector<uint8_t> response;
|
std::vector<uint8_t> response;
|
||||||
if (!readN(expectedRespLen, response))
|
if (!readN(expectedRespLen, response))
|
||||||
{
|
{
|
||||||
log_e("Failed receive readCoils response");
|
log_e("Failed receive readInteger response");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,48 +228,130 @@ namespace drivers
|
|||||||
const std::vector<uint8_t> respData(response.begin() + RESP_HEADER_SIZE, response.end() - RESP_CRC_SIZE);
|
const std::vector<uint8_t> respData(response.begin() + RESP_HEADER_SIZE, response.end() - RESP_CRC_SIZE);
|
||||||
for (auto i(0); i < nRespDataBytes; i++)
|
for (auto i(0); i < nRespDataBytes; i++)
|
||||||
{
|
{
|
||||||
reg_bytes_t val;
|
const uint8_t hi(respData.at(i * sizeof(uint16_t)));
|
||||||
val.hi = respData.at(i * sizeof(uint16_t));
|
const uint8_t lo(respData.at(1 + i * sizeof(uint16_t)));
|
||||||
val.lo = respData.at(1 + i * sizeof(uint16_t));
|
const uint16_t val(0xFFFF & ((hi << 8) | lo));
|
||||||
out.push_back(be16toh(val.reg16));
|
out.push_back(be16toh(val));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Func 0x05
|
const bool MODBUS::writeBinary(const uint8_t func, const uint8_t device, const uint16_t reg, const uint16_t bits, const std::vector<bool> &in)
|
||||||
const bool MODBUS::writeCoil(const uint8_t device, const uint16_t coil, const bool value)
|
|
||||||
{
|
{
|
||||||
|
std::vector<uint16_t> bitsOut;
|
||||||
|
if (bits <= 1) // if single coil value must be 0x00FF[00] for on[off]
|
||||||
|
{
|
||||||
|
bitsOut.push_back(htobe16(in.front() ? 0x00FF : 0x0000));
|
||||||
|
}
|
||||||
|
else // if multiple coils value is 0x01 shifted for the number of coil intended
|
||||||
|
{
|
||||||
|
const uint16_t numRegisters((uint16_t)(bits / 16) + 1);
|
||||||
|
bitsOut.resize(numRegisters, 0);
|
||||||
|
for (uint16_t i(0); i < in.size(); i++)
|
||||||
|
{
|
||||||
|
if (!in[i]) // if value is false skip
|
||||||
|
continue;
|
||||||
|
const uint16_t curReg(i / 16);
|
||||||
|
bitsOut[curReg] |= 0x01 << i % 16;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!write(multiRequest(device, func, reg, bits, bitsOut)))
|
||||||
|
{
|
||||||
|
log_e("Failed send writeBinary command");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint16_t expectedRespLen(sizeof(resp_t));
|
||||||
|
std::vector<uint8_t> response;
|
||||||
|
if (!readN(expectedRespLen, response))
|
||||||
|
{
|
||||||
|
log_e("Failed receive writeBinary response");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute crc of current message
|
||||||
|
if (!verifyCrc(response))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Func 0x06
|
const bool MODBUS::writeInteger(const uint8_t func, const uint8_t device, const uint16_t reg, const uint16_t num, const std::vector<uint16_t> &in)
|
||||||
const bool MODBUS::writeRegister(const uint8_t device, const uint16_t reg, const uint16_t value)
|
|
||||||
{
|
{
|
||||||
|
if (!write(multiRequest(device, func, reg, num, in)))
|
||||||
|
{
|
||||||
|
log_e("Failed send writeInteger command");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint16_t expectedRespLen(sizeof(resp_t));
|
||||||
|
std::vector<uint8_t> response;
|
||||||
|
if (!readN(expectedRespLen, response))
|
||||||
|
{
|
||||||
|
log_e("Failed receive writeInteger response");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute crc of current message
|
||||||
|
if (!verifyCrc(response))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Func 0x0F
|
const std::vector<uint8_t> MODBUS::singleRequest(const uint8_t device, const uint8_t func, const uint16_t reg, const uint16_t data)
|
||||||
const bool MODBUS::writeCoils(const uint8_t device, const uint16_t reg, const std::vector<bool> &coils)
|
|
||||||
{
|
{
|
||||||
}
|
req_t header;
|
||||||
|
header.device = device;
|
||||||
|
header.func = func;
|
||||||
|
header.reg = htobe16(reg);
|
||||||
|
header.data = htobe16(data);
|
||||||
|
|
||||||
// Func 0x10
|
const uint8_t headerBytes(sizeof(req_t));
|
||||||
const bool MODBUS::writeRegisters(const uint8_t device, const uint16_t reg, const std::vector<uint16_t> &values)
|
const uint8_t crcBytes(sizeof(crc_t));
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<uint8_t> MODBUS::buildRequest(const uint8_t device, const uint8_t func, const uint16_t reg, const uint16_t qty)
|
|
||||||
{
|
|
||||||
req_t msg;
|
|
||||||
msg.device = device;
|
|
||||||
msg.func = func;
|
|
||||||
msg.reg.reg16 = htobe16(reg);
|
|
||||||
msg.qty.reg16 = htobe16(qty);
|
|
||||||
|
|
||||||
|
// compute crc for header + data
|
||||||
m_crc.reset();
|
m_crc.reset();
|
||||||
m_crc.add((uint8_t *)&msg, sizeof(req_t));
|
m_crc.add((uint8_t *)&header, headerBytes); // exclude last two bytes of crc
|
||||||
msg.crc = m_crc.getCRC();
|
const uint16_t crc(htole16(m_crc.getCRC()));
|
||||||
|
|
||||||
std::vector<uint8_t> data(sizeof(req_t), 0);
|
std::vector<uint8_t> dataOut(headerBytes + crcBytes, 0);
|
||||||
std::memcpy(data.data(), &msg, sizeof(req_t));
|
std::memcpy(dataOut.data(), &header, headerBytes);
|
||||||
return data;
|
std::memcpy(dataOut.data() + headerBytes, &crc, crcBytes);
|
||||||
|
return dataOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<uint8_t> MODBUS::multiRequest(const uint8_t device, const uint8_t func, const uint16_t reg, const uint16_t qty, const std::vector<uint16_t> &data)
|
||||||
|
{
|
||||||
|
req_multi_t header;
|
||||||
|
header.device = device;
|
||||||
|
header.func = func;
|
||||||
|
header.reg = htobe16(reg);
|
||||||
|
header.qty = htobe16(qty);
|
||||||
|
header.bytes = (uint8_t)(data.size() * sizeof(uint16_t)); // 8 bit value
|
||||||
|
|
||||||
|
// convert uint16_t values from host endianness to big endian
|
||||||
|
std::vector<uint16_t> dataBe;
|
||||||
|
dataBe.reserve(data.size());
|
||||||
|
std::for_each(data.begin(), data.end(), [&dataBe](auto v)
|
||||||
|
{ dataBe.push_back(htobe16(v)); });
|
||||||
|
|
||||||
|
const uint8_t headerBytes(sizeof(req_multi_t));
|
||||||
|
const uint8_t dataBytes(sizeof(uint16_t) * dataBe.size());
|
||||||
|
const uint8_t crcBytes(sizeof(crc_t));
|
||||||
|
|
||||||
|
// compute crc for header + data
|
||||||
|
m_crc.reset();
|
||||||
|
m_crc.add((uint8_t *)&header, headerBytes); // add the request excluding the CRC code
|
||||||
|
m_crc.add((uint8_t *)dataBe.data(), dataBytes);
|
||||||
|
const uint16_t crc(htole16(m_crc.getCRC()));
|
||||||
|
|
||||||
|
std::vector<uint8_t> dataOut;
|
||||||
|
dataOut.resize(headerBytes + dataBytes + crcBytes); // header message + data values + crc code
|
||||||
|
std::memcpy(dataOut.data(), &header, headerBytes); // copy message
|
||||||
|
std::memcpy(dataOut.data() + headerBytes, dataBe.data(), dataBytes); // copy data
|
||||||
|
std::memcpy(dataOut.data() + headerBytes + dataBytes, &crc, crcBytes); // copy crc
|
||||||
|
return dataOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool MODBUS::verifyCrc(const std::vector<uint8_t> &data)
|
const bool MODBUS::verifyCrc(const std::vector<uint8_t> &data)
|
||||||
@@ -381,13 +361,14 @@ namespace drivers
|
|||||||
m_crc.add(data.data(), data.size());
|
m_crc.add(data.data(), data.size());
|
||||||
const uint16_t computedCrc(m_crc.getCRC());
|
const uint16_t computedCrc(m_crc.getCRC());
|
||||||
// extract crc from response
|
// extract crc from response
|
||||||
const uint8_t crcHi(data.back());
|
const uint16_t size(data.size());
|
||||||
const uint8_t crcLo(data.back() - 1);
|
const uint8_t crcLo(data.at(size - 2));
|
||||||
|
const uint8_t crcHi(data.at(size - 1));
|
||||||
|
|
||||||
// verify crc code
|
// verify crc code
|
||||||
if (highByte(computedCrc) != crcHi || lowByte(computedCrc) != crcLo)
|
if (highByte(computedCrc) != crcHi || lowByte(computedCrc) != crcLo)
|
||||||
{
|
{
|
||||||
log_e("Failed verify CRC code: comp[%04x], rec[%04x]", computedCrc, 0xFFFF && ((crcHi << 8) || crcLo));
|
log_e("Failed verify CRC code: comp[%04x], rec[%04x]", computedCrc, 0xFFFF & ((crcHi << 8) | crcLo));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -46,23 +46,28 @@ namespace drivers
|
|||||||
{
|
{
|
||||||
|
|
||||||
static const uint8_t RESP_HEADER_SIZE = 3;
|
static const uint8_t RESP_HEADER_SIZE = 3;
|
||||||
static const uint8_t RESP_CRC_SIZE = 1;
|
static const uint8_t RESP_CRC_SIZE = 2;
|
||||||
|
|
||||||
typedef union {
|
|
||||||
uint8_t hi;
|
|
||||||
uint8_t lo;
|
|
||||||
uint16_t reg16;
|
|
||||||
} reg_bytes_t;
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
uint8_t device;
|
uint8_t device;
|
||||||
uint8_t func;
|
uint8_t func;
|
||||||
reg_bytes_t reg;
|
uint16_t reg;
|
||||||
reg_bytes_t qty;
|
uint16_t data;
|
||||||
uint16_t crc = 0xFF;
|
|
||||||
} req_t;
|
} req_t;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
uint8_t device;
|
||||||
|
uint8_t func;
|
||||||
|
uint16_t reg;
|
||||||
|
uint16_t qty;
|
||||||
|
uint8_t bytes;
|
||||||
|
} req_multi_t;
|
||||||
|
|
||||||
|
typedef req_t resp_t;
|
||||||
|
typedef uint16_t crc_t;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MODBUS(const uint32_t baud, const SerialConfig conf);
|
MODBUS(const uint32_t baud, const SerialConfig conf);
|
||||||
|
|
||||||
@@ -85,16 +90,19 @@ namespace drivers
|
|||||||
const bool writeRegister(const uint8_t device, const uint16_t reg, const uint16_t value);
|
const bool writeRegister(const uint8_t device, const uint16_t reg, const uint16_t value);
|
||||||
|
|
||||||
// Func 0x0F
|
// Func 0x0F
|
||||||
const bool writeCoils(const uint8_t device, const uint16_t reg, const std::vector<bool> &coils);
|
const bool writeCoils(const uint8_t device, const uint16_t coils, const std::vector<bool> &values);
|
||||||
|
|
||||||
// Func 0x10
|
// Func 0x10
|
||||||
const bool writeRegisters(const uint8_t device, const uint16_t reg, const std::vector<uint16_t> &values);
|
const bool writeRegisters(const uint8_t device, const uint16_t reg, const std::vector<uint16_t> &values);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CRC16 m_crc;
|
CRC16 m_crc;
|
||||||
const std::vector<uint8_t> buildRequest(const uint8_t device, const uint8_t func, const uint16_t reg, const uint16_t qty);
|
const std::vector<uint8_t> singleRequest(const uint8_t device, const uint8_t func, const uint16_t reg, const uint16_t data);
|
||||||
|
const std::vector<uint8_t> multiRequest(const uint8_t device, const uint8_t func, const uint16_t reg, const uint16_t qty, const std::vector<uint16_t> &data);
|
||||||
const bool readBinary(const uint8_t func, const uint8_t device, const uint16_t reg, const uint16_t bits, std::vector<bool> &out);
|
const bool readBinary(const uint8_t func, const uint8_t device, const uint16_t reg, const uint16_t bits, std::vector<bool> &out);
|
||||||
const bool readInteger(const uint8_t func, const uint8_t device, const uint16_t reg, const uint16_t num, std::vector<uint16_t> &out);
|
const bool readInteger(const uint8_t func, const uint8_t device, const uint16_t reg, const uint16_t num, std::vector<uint16_t> &out);
|
||||||
|
const bool writeBinary(const uint8_t func, const uint8_t device, const uint16_t reg, const uint16_t bits, const std::vector<bool> &in);
|
||||||
|
const bool writeInteger(const uint8_t func, const uint8_t device, const uint16_t reg, const uint16_t num, const std::vector<uint16_t> &in);
|
||||||
const bool verifyCrc(const std::vector<uint8_t> &data);
|
const bool verifyCrc(const std::vector<uint8_t> &data);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
163
src/WS_RS485.cpp
163
src/WS_RS485.cpp
@@ -1,163 +0,0 @@
|
|||||||
#include "WS_RS485.h"
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
HardwareSerial lidarSerial(1); // Using serial port 1
|
|
||||||
uint8_t data[][8] = { // ESP32-S3-POE-ETH-8DI-8RO Control Command (RS485 receiving data)
|
|
||||||
{ 0x06, 0x05, 0x00, 0x01, 0x55, 0x00, 0xA2, 0xED }, // ESP32-S3-POE-ETH-8DI-8RO CH1 Toggle
|
|
||||||
{ 0x06, 0x05, 0x00, 0x02, 0x55, 0x00, 0x52, 0xED }, // ESP32-S3-POE-ETH-8DI-8RO CH2 Toggle
|
|
||||||
{ 0x06, 0x05, 0x00, 0x03, 0x55, 0x00, 0x03, 0x2D }, // ESP32-S3-POE-ETH-8DI-8RO CH3 Toggle
|
|
||||||
{ 0x06, 0x05, 0x00, 0x04, 0x55, 0x00, 0xB2, 0xEC }, // ESP32-S3-POE-ETH-8DI-8RO CH4 Toggle
|
|
||||||
{ 0x06, 0x05, 0x00, 0x05, 0x55, 0x00, 0xE3, 0x2C }, // ESP32-S3-POE-ETH-8DI-8RO CH5 Toggle
|
|
||||||
{ 0x06, 0x05, 0x00, 0x06, 0x55, 0x00, 0x13, 0x2C }, // ESP32-S3-POE-ETH-8DI-8RO CH6 Toggle
|
|
||||||
{ 0x06, 0x05, 0x00, 0x07, 0x55, 0x00, 0x42, 0xEC }, // ESP32-S3-POE-ETH-8DI-8RO CH7 Toggle
|
|
||||||
{ 0x06, 0x05, 0x00, 0x08, 0x55, 0x00, 0x72, 0xEF }, // ESP32-S3-POE-ETH-8DI-8RO CH8 Toggle
|
|
||||||
{ 0x06, 0x05, 0x00, 0xFF, 0xFF, 0x00, 0xBD, 0xBD }, // ESP32-S3-POE-ETH-8DI-8RO ALL ON
|
|
||||||
{ 0x06, 0x05, 0x00, 0xFF, 0x00, 0x00, 0xFC, 0x4D }, // ESP32-S3-POE-ETH-8DI-8RO ALL OFF
|
|
||||||
};
|
|
||||||
uint8_t Send_Data[][8] = { // Modbus RTU Relay Control Command (RS485 send data)
|
|
||||||
{ 0x01, 0x05, 0x00, 0x00, 0x55, 0x00, 0xF2, 0x9A }, // Modbus RTU Relay CH1 Toggle
|
|
||||||
{ 0x01, 0x05, 0x00, 0x01, 0x55, 0x00, 0xA3, 0x5A }, // Modbus RTU Relay CH2 Toggle
|
|
||||||
{ 0x01, 0x05, 0x00, 0x02, 0x55, 0x00, 0x53, 0x5A }, // Modbus RTU Relay CH3 Toggle
|
|
||||||
{ 0x01, 0x05, 0x00, 0x03, 0x55, 0x00, 0x02, 0x9A }, // Modbus RTU Relay CH4 Toggle
|
|
||||||
{ 0x01, 0x05, 0x00, 0x04, 0x55, 0x00, 0xB3, 0x5B }, // Modbus RTU Relay CH5 Toggle
|
|
||||||
{ 0x01, 0x05, 0x00, 0x05, 0x55, 0x00, 0xE2, 0x9B }, // Modbus RTU Relay CH6 Toggle
|
|
||||||
{ 0x01, 0x05, 0x00, 0x06, 0x55, 0x00, 0x12, 0x9B }, // Modbus RTU Relay CH7 Toggle
|
|
||||||
{ 0x01, 0x05, 0x00, 0x07, 0x55, 0x00, 0x43, 0x5B }, // Modbus RTU Relay CH8 Toggle
|
|
||||||
{ 0x01, 0x05, 0x00, 0xFF, 0xFF, 0xFF, 0xFC, 0x4A }, // Modbus RTU Relay ALL ON
|
|
||||||
{ 0x01, 0x05, 0x00, 0xFF, 0x00, 0x00, 0xFD, 0xFA }, // Modbus RTU Relay ALL OFF
|
|
||||||
};
|
|
||||||
uint8_t buf[20] = {0}; // Data storage area
|
|
||||||
int numRows = sizeof(data) / sizeof(data[0]);
|
|
||||||
|
|
||||||
void SetData(uint8_t* data, size_t length) {
|
|
||||||
lidarSerial.write(data, length); // Send data from the RS485
|
|
||||||
}
|
|
||||||
void ReadData(uint8_t* buf, uint8_t length) {
|
|
||||||
uint8_t Receive_Flag = 0;
|
|
||||||
Receive_Flag = lidarSerial.available();
|
|
||||||
if (Receive_Flag >= length) {
|
|
||||||
lidarSerial.readBytes(buf, length);
|
|
||||||
char printBuf[length * 3 + 1];
|
|
||||||
sprintf(printBuf, "Received data: ");
|
|
||||||
for (int i = 0; i < length; i++) {
|
|
||||||
sprintf(printBuf + strlen(printBuf), "%02X ", buf[i]);
|
|
||||||
}
|
|
||||||
printf(printBuf);
|
|
||||||
/*************************
|
|
||||||
Add a receiving data handler
|
|
||||||
*************************/
|
|
||||||
Receive_Flag = 0;
|
|
||||||
memset(buf, 0, sizeof(buf));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void RS485_Analysis(uint8_t *buf)
|
|
||||||
{
|
|
||||||
switch(buf[1])
|
|
||||||
{
|
|
||||||
case Extension_CH1:
|
|
||||||
SetData(Send_Data[0],sizeof(Send_Data[0]));
|
|
||||||
printf("|*** Toggle expansion channel 1 ***|\r\n");
|
|
||||||
break;
|
|
||||||
case Extension_CH2:
|
|
||||||
SetData(Send_Data[1],sizeof(Send_Data[1]));
|
|
||||||
printf("|*** Toggle expansion channel 2 ***|\r\n");
|
|
||||||
break;
|
|
||||||
case Extension_CH3:
|
|
||||||
SetData(Send_Data[2],sizeof(Send_Data[2]));
|
|
||||||
printf("|*** Toggle expansion channel 3 ***|\r\n");
|
|
||||||
break;
|
|
||||||
case Extension_CH4:
|
|
||||||
SetData(Send_Data[3],sizeof(Send_Data[3]));
|
|
||||||
printf("|*** Toggle expansion channel 4 ***|\r\n");
|
|
||||||
break;
|
|
||||||
case Extension_CH5:
|
|
||||||
SetData(Send_Data[4],sizeof(Send_Data[4]));
|
|
||||||
printf("|*** Toggle expansion channel 5 ***|\r\n");
|
|
||||||
break;
|
|
||||||
case Extension_CH6:
|
|
||||||
SetData(Send_Data[5],sizeof(Send_Data[5]));
|
|
||||||
printf("|*** Toggle expansion channel 6 ***|\r\n");
|
|
||||||
break;
|
|
||||||
case Extension_CH7:
|
|
||||||
SetData(Send_Data[6],sizeof(Send_Data[6]));
|
|
||||||
printf("|*** Toggle expansion channel 7 ***|\r\n");
|
|
||||||
break;
|
|
||||||
case Extension_CH8:
|
|
||||||
SetData(Send_Data[7],sizeof(Send_Data[7]));
|
|
||||||
printf("|*** Toggle expansion channel 8 ***|\r\n");
|
|
||||||
break;
|
|
||||||
case Extension_ALL_ON:
|
|
||||||
SetData(Send_Data[8],sizeof(Send_Data[8]));
|
|
||||||
printf("|*** Enable all extension channels ***|\r\n");
|
|
||||||
break;
|
|
||||||
case Extension_ALL_OFF:
|
|
||||||
SetData(Send_Data[9],sizeof(Send_Data[9]));
|
|
||||||
printf("|*** Close all expansion channels ***|\r\n");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
printf("Note : Non-control external device instructions !\r\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
uint32_t Baudrate = 0;
|
|
||||||
double transmission_time = 0;
|
|
||||||
double RS485_cmd_Time = 0;
|
|
||||||
void RS485_Init() // Initializing serial port
|
|
||||||
{
|
|
||||||
Baudrate = 9600; // Set the baud rate of the serial port
|
|
||||||
lidarSerial.begin(Baudrate, SERIAL_8N1, RXD1, TXD1); // Initializing serial port
|
|
||||||
transmission_time = 10.0 / Baudrate * 1000 ;
|
|
||||||
RS485_cmd_Time = transmission_time*8; // 8:data length
|
|
||||||
xTaskCreatePinnedToCore(
|
|
||||||
RS485Task,
|
|
||||||
"RS485Task",
|
|
||||||
4096,
|
|
||||||
NULL,
|
|
||||||
3,
|
|
||||||
NULL,
|
|
||||||
0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RS485Task(void *parameter) {
|
|
||||||
while(1){
|
|
||||||
RS485_Loop();
|
|
||||||
vTaskDelay(pdMS_TO_TICKS(50));
|
|
||||||
}
|
|
||||||
vTaskDelete(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void RS485_Loop()
|
|
||||||
{
|
|
||||||
uint8_t Receive_Flag = 0; // Receiving mark
|
|
||||||
Receive_Flag = lidarSerial.available();
|
|
||||||
|
|
||||||
if (Receive_Flag > 0) {
|
|
||||||
if(RS485_cmd_Time > 1) // Time greater than 1 millisecond
|
|
||||||
delay((uint16_t)RS485_cmd_Time);
|
|
||||||
else // Time is less than 1 millisecond
|
|
||||||
delay(1);
|
|
||||||
Receive_Flag = lidarSerial.available();
|
|
||||||
lidarSerial.readBytes(buf, Receive_Flag); // The Receive_Flag length is read
|
|
||||||
if(Receive_Flag == 8){
|
|
||||||
uint8_t i=0;
|
|
||||||
for(i=0;i<numRows;i++){
|
|
||||||
bool result = std::equal(std::begin(buf), std::begin(buf) + 8, std::begin(data[i])); // Compare two arrays
|
|
||||||
if(result){
|
|
||||||
if(i < numRows-1)
|
|
||||||
buf[0] = i+1+48;
|
|
||||||
else if(i == numRows-1)
|
|
||||||
buf[0] = 48;
|
|
||||||
Relay_Analysis(buf,RS485_Mode);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(i > numRows-1)
|
|
||||||
printf("Note : Non-instruction data was received - RS485 !\r\n");
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
printf("Note : Non-instruction data was received .Number of bytes: %d - RS485 !\r\n",Receive_Flag);
|
|
||||||
}
|
|
||||||
Receive_Flag=0;
|
|
||||||
memset(buf,0, sizeof(buf));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <HardwareSerial.h> // Reference the ESP32 built-in serial port library
|
|
||||||
#include "WS_GPIO.h"
|
|
||||||
#include "WS_Relay.h"
|
|
||||||
|
|
||||||
#define Extension_CH1 1 // Expansion Channel 1
|
|
||||||
#define Extension_CH2 2 // Expansion Channel 2
|
|
||||||
#define Extension_CH3 3 // Expansion Channel 3
|
|
||||||
#define Extension_CH4 4 // Expansion Channel 4
|
|
||||||
#define Extension_CH5 5 // Expansion Channel 5
|
|
||||||
#define Extension_CH6 6 // Expansion Channel 6
|
|
||||||
#define Extension_CH7 7 // Expansion Channel 7
|
|
||||||
#define Extension_CH8 8 // Expansion Channel 8
|
|
||||||
#define Extension_ALL_ON 9 // Expansion ALL ON
|
|
||||||
#define Extension_ALL_OFF 10 // Expansion ALL OFF
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void SetData(uint8_t* data, size_t length); // Send data from the RS485
|
|
||||||
void ReadData(uint8_t* buf, uint8_t length); // Data is received over RS485
|
|
||||||
|
|
||||||
void RS485_Analysis(uint8_t *buf); // External relay control
|
|
||||||
void RS485_Init(); // Example Initialize the system serial port and RS485
|
|
||||||
void RS485_Loop(); // Read RS485 data, parse and control relays
|
|
||||||
void RS485Task(void *parameter);
|
|
||||||
Reference in New Issue
Block a user