I have a small project in C++. Reading the COM port. I have code that opens a COM port and reads values from it. When compressing the buffer during checksum check with the generated checksum the values do not match.
It means that
if (checksum != generated_checksum) {
return;
}
is fulfilled.
This is my code.
#include <boost/asio.hpp>
#include <iostream>
using namespace std::chrono_literals;
namespace asio = boost::asio;
using std::this_thread::sleep_for;
// mocking question code
struct {
void info(std::string const& msg) const {
static auto const start = std::chrono::steady_clock::now();
auto ts = (std::chrono::steady_clock::now() - start);
if (ts > 1s)
std::cout << std::setw(6) << ts / 1.s << "s " << msg << std::endl;
else
std::cout << std::setw(6) << ts / 1ms << "ms " << msg << std::endl;
}
} static g_logger;
// end mocks
struct COM {
asio::thread_pool m_ioc{1};
asio::serial_port m_port{m_ioc};
std::atomic_int m_Data = 0;
std::weak_ptr<void> m_readoperation; // only when read operation active
void ReadLoop(std::shared_ptr<std::vector<unsigned char>> buffer = {}) {
if (!buffer) {
assert(m_readoperation.expired()); // do not post overlapping read operations
buffer = std::make_shared<std::vector<unsigned char>>();
m_readoperation = buffer;
}
asio::async_read(
m_port, asio::dynamic_buffer(*buffer), asio::transfer_at_least(1),
[this, buffer](boost::system::error_code ec, size_t /*length*/) {
if (!ec) {
try {
// Directly process binary data from buffer here.
size_t length = buffer->size();
m_Data.store(length);
// Example processing based on your description
for (size_t i = 0; i < length; ++i) {
if (i + 2 < buffer->size() && (*buffer)[i] == 170 && (*buffer)[i + 1] == 170) {
size_t packetLength = (*buffer)[i + 2];
if (packetLength < 4 || packetLength >= 170) {
continue; // Invalid packet length, move to next byte.
}
if (i + 2 + packetLength <= buffer->size()) {
// Assume parse_packet processes a packet starting from a specific index
// and uses packetLength to determine the end.
// This function needs to be adapted to accept a start index and length,
// or you need to create a sub-vector to pass to it.
parse_packet({buffer->begin() + i, buffer->begin() + i + packetLength});
i += packetLength - 1; // Move to the end of the packet.
}
}
}
// Clear buffer after processing, or handle remaining bytes as needed.
buffer->clear();
// Continue reading into the same buffer for new data.
ReadLoop(buffer);
} catch (const std::exception& e) {
g_logger.info("Error processing received data: " + std::string(e.what()));
}
} else {
// Handle read errors.
g_logger.info("ReadLoop error: " + ec.message());
}
});
}
void parse_packet(std::vector<unsigned char> const& data) {
if (data.size() < 4)
return; // Minimal packet length
int generated_checksum = 0;
for (size_t i = 0; i < data.size() - 1; ++i) { // Exclude the last byte, which is the checksum
generated_checksum += data[i];
}
generated_checksum = 255 - (generated_checksum % 256);
int checksum = data.back(); // Assume the last byte is the checksum
g_logger.info("Checksum: " + std::to_string(checksum) +
", Generated checksum: " + std::to_string(generated_checksum));
if (checksum != generated_checksum) {
g_logger.info("Checksum failed ");
return; // Checksum mismatch
}
// Iterate through packet data, excluding the checksum
size_t i = 0;
while (i < data.size() - 1) {
unsigned char data_type = data[i++];
switch (data_type) {
case 1: { // Battery level
//..rest of code..//
//
}
}
}
}
void initialize(bool enable = true) {
sleep_for(100ms);
m_port.open("COM5");
// Configure serial port settings
using P = asio::serial_port;
m_port.set_option(P::baud_rate(115200));
m_port.set_option(P::character_size(8));
m_port.set_option(P::parity(P::parity::none));
m_port.set_option(P::stop_bits(P::stop_bits::one));
m_port.set_option(P::flow_control(P::flow_control::none));
g_logger.info("Connected");
setEnabled(enable);
}
void setEnabled(bool enable) {
if (enable == !m_readoperation.expired())
return; // nothing to do
if (!enable)
m_port.cancel(); // cancels read operation (asio::error::operation_aborted)
else
ReadLoop();
}
void terminate() {
setEnabled(false);
g_logger.info("Bye!");
}
};
// demo code
int main() {
std::cout << std::fixed << std::setprecision(2);
g_logger.info("Demo start");
COM g_com;
g_com.initialize(false);
for (auto i = 0; i < 10; ++i) {
sleep_for(3.5s);
g_logger.info("Main thread observed atomic int value: " + to_string(g_com.m_Data));
bool enable = i % 2; // enable if odd, disable if even
g_logger.info(enable ? "Enabling read operation" : "Disabling read operation");
g_com.setEnabled(i % 2);
}
g_com.terminate();
}
This is output
Checksum: 128, Generated checksum: 167
Checksum failed
Checksum: 128, Generated checksum: 167
Checksum failed
Checksum: 128, Generated checksum: 167
Checksum failed
Checksum: 128, Generated checksum: 167
Checksum failed
The checksum does not change at all and neither does the generated checksum.
In Python, checksums look like this:
91 , 91
109 , 109
114 , 114
83 , 83
38 , 38
25 , 25
45 , 45
57 , 57
76 , 76
82 , 82
83 , 83
84 , 84
67 , 67
69 , 69
100 , 100
I can't find the cause of the checksum error
Python Code:
def receive_byte():
global CurrentPosition
if CurrentPosition >= len(ReceivedString):
return 0
result = ReceivedString[CurrentPosition]
CurrentPosition += 1
return result
def skip_byte():
global CurrentPosition
CurrentPosition += 1
def drop_parsing():
global UnparsedRemainingString, ReceivedString, CurrentPosition
UnparsedRemainingString = b""
if CurrentPosition > 3:
UnparsedRemainingString = ReceivedString[CurrentPosition-3:]
if len(UnparsedRemainingString) > 128:
UnparsedRemainingString = b""
ReceivedString = b""
def parse_packet(length):
global Raw_count, needSave, SaveFile
generated_checksum = 0
payload_data = [0 for _ in range(171)] # Initializing payloadData array
for i in range(length):
payload_data[i] = receive_byte()
generated_checksum += payload_data[i]
generated_checksum = 255 - generated_checksum % 256
checksum = receive_byte()
print(checksum,",", generated_checksum)
if checksum != generated_checksum:
return
if length < 4:
return
i = 0
while i < length - 1:
data_type = payload_data[i]
i += 1
if data_type == 1: # battery level
battery_level = payload_data[i]
i += 1
print(f"battery_level: {battery_level}")
//....//
def read_data_from_serial(port='/dev/ttyUSB0', baudrate=9600):
global ReceivedString, CurrentPosition, UnparsedRemainingString
ser = serial.Serial(port, baudrate, timeout=1)
while True:
data = ser.read(ser.in_waiting or 1)
if data:
ReceivedString = UnparsedRemainingString + data
CurrentPosition = 0
while CurrentPosition < len(ReceivedString) - 2:
if receive_byte() == 170 and receive_byte() == 170:
length = receive_byte()
if length < 4 or length >= 170:
continue
if CurrentPosition + length > len(ReceivedString):
drop_parsing()
break
else:
parse_packet(length)
if __name__ == "__main__":
read_data_from_serial('COM6', 115200) # Example usage
What am I doing wrong?
Reverse engineering from the C++ code, your message structure is:
\xaa \xaa LEN DATA[LEN]
When matching "\xaa\xaa" at i
you pass only the buffer range [i, i+LEN)
to parser_packet
. That's 3 bytes short. This fixes it for me:
asio::async_read(
m_port, asio::dynamic_buffer(*pbuf), asio::transfer_at_least(1),
[this, pbuf](boost::system::error_code ec, size_t /*length*/) {
if (!ec) {
try {
// Directly process binary data from buffer here.
auto& buffer = *pbuf;
size_t const length = buffer.size();
m_Data.store(length);
// message \xaa \xaa LEN DATA[LEN]
// Example processing based on your description
if (length > 2)
for (size_t i = 0; i < length - 2; ++i) {
if (buffer[i] == 170 && buffer[i + 1] == 170) {
size_t packetLength = buffer[i + 2];
if (packetLength < 4 || packetLength >= 170) {
continue; // Invalid packet length, move to next byte.
}
if (i + 3 + packetLength <= buffer.size()) {
parse_packet({buffer.begin() + i + 3, packetLength});
i += packetLength - 1; // Move to the end of the packet.
}
}
}
// Clear buffer after processing, or handle remaining bytes as needed.
buffer.clear();
// Continue reading into the same buffer for new data.
ReadLoop(pbuf);
} catch (const std::exception& e) {
g_logger.info("Error processing received data: " + std::string(e.what()));
}
} else {
// Handle read errors.
g_logger.info("ReadLoop error: " + ec.message());
}
});
That is to say, when testing with a payload of "\xaa\xaa\x04abc\xd9"
it decodes back to 217 (0xd9) correctly.
I also suggest using span
to pass to parse_packet
instead of copying the data:
void parse_packet(std::span<uint8_t const> data) {
Live On Coliru
#include <boost/asio.hpp>
#include <iostream>
using namespace std::chrono_literals;
using namespace std::string_literals;
namespace asio = boost::asio;
using std::this_thread::sleep_for;
// mocking question code
struct {
void info(std::string const& msg) const {
static auto const start = std::chrono::steady_clock::now();
auto ts = (std::chrono::steady_clock::now() - start);
if (ts > 1s)
std::cout << std::setw(6) << ts / 1.s << "s " << msg << std::endl;
else
std::cout << std::setw(6) << ts / 1ms << "ms " << msg << std::endl;
}
} static g_logger;
// end mocks
struct COM {
asio::thread_pool m_ioc{1};
asio::serial_port m_port{m_ioc};
std::atomic_int m_Data = 0;
std::weak_ptr<void> m_readoperation; // only when read operation active
~COM() { m_ioc.join(); }
void ReadLoop(std::shared_ptr<std::vector<unsigned char>> pbuf = {}) {
g_logger.info("Readloop" + (pbuf ? " with buffer"s : ""));
if (!pbuf) {
assert(m_readoperation.expired()); // do not post overlapping read operations
pbuf = std::make_shared<std::vector<unsigned char>>();
m_readoperation = pbuf;
}
asio::async_read(
m_port, asio::dynamic_buffer(*pbuf), asio::transfer_at_least(1),
[this, pbuf](boost::system::error_code ec, size_t /*length*/) {
if (!ec) {
try {
// Directly process binary data from buffer here.
auto& buffer = *pbuf;
size_t const length = buffer.size();
m_Data.store(length);
// message \xaa \xaa LEN DATA[LEN]
// Example processing based on your description
if (length > 2)
for (size_t i = 0; i < length - 2; ++i) {
if (buffer[i] == 170 && buffer[i + 1] == 170) {
size_t packetLength = buffer[i + 2];
if (packetLength < 4 || packetLength >= 170) {
continue; // Invalid packet length, move to next byte.
}
if (i + 3 + packetLength <= buffer.size()) {
parse_packet({buffer.begin() + i + 3, packetLength});
i += packetLength - 1; // Move to the end of the packet.
}
}
}
// Clear buffer after processing, or handle remaining bytes as needed.
buffer.clear();
// Continue reading into the same buffer for new data.
ReadLoop(pbuf);
} catch (const std::exception& e) {
g_logger.info("Error processing received data: " + std::string(e.what()));
}
} else {
// Handle read errors.
g_logger.info("ReadLoop error: " + ec.message());
}
});
}
void parse_packet(std::span<uint8_t const> data) {
if (data.size() < 4)
return; // Minimal packet length
int generated_checksum = 0;
for (size_t i = 0; i < data.size() - 1; ++i) { // Exclude the last byte, which is the checksum
generated_checksum += data[i];
}
generated_checksum = 255 - (generated_checksum % 256);
int checksum = data.back(); // Assume the last byte is the checksum
g_logger.info("Checksum: " + std::to_string(checksum) +
", Generated checksum: " + std::to_string(generated_checksum));
if (checksum != generated_checksum) {
g_logger.info("Checksum failed ");
return; // Checksum mismatch
}
// Iterate through packet data, excluding the checksum
size_t i = 0;
while (i < data.size() - 1) {
unsigned char data_type = data[i++];
switch (data_type) {
case 1: { // Battery level
//..rest of code..//
//
}
}
}
}
void initialize(bool enable = true) {
sleep_for(100ms);
m_port.open("COM5");
// Configure serial port settings
using P = asio::serial_port;
m_port.set_option(P::baud_rate(115200));
m_port.set_option(P::character_size(8));
m_port.set_option(P::parity(P::parity::none));
m_port.set_option(P::stop_bits(P::stop_bits::one));
m_port.set_option(P::flow_control(P::flow_control::none));
g_logger.info("Connected");
setEnabled(enable);
}
void setEnabled(bool enable) {
if (enable == !m_readoperation.expired())
return; // nothing to do
if (!enable)
m_port.cancel(); // cancels read operation (asio::error::operation_aborted)
else
ReadLoop();
}
void terminate() {
setEnabled(false);
g_logger.info("Bye!");
}
};
// demo code
int main() {
std::cout << std::fixed << std::setprecision(2);
g_logger.info("Demo start");
COM g_com;
g_com.initialize();
// g_com.terminate();
}