Added inline comments to functions
This commit is contained in:
@@ -17,19 +17,28 @@
|
|||||||
*/
|
*/
|
||||||
void SerialConnector::cycle()
|
void SerialConnector::cycle()
|
||||||
{
|
{
|
||||||
|
// Only proceed if there is at least a minimal number of bytes
|
||||||
|
// (message must include command, separators and checksum).
|
||||||
if (Serial.available() > 3)
|
if (Serial.available() > 3)
|
||||||
{
|
{
|
||||||
|
// Read until message terminator '@' (terminator is removed by readStringUntil).
|
||||||
String raw = Serial.readStringUntil('@');
|
String raw = Serial.readStringUntil('@');
|
||||||
|
|
||||||
|
// Parse message parts: command and args.
|
||||||
String cmd = getCommandFromIncomming(raw);
|
String cmd = getCommandFromIncomming(raw);
|
||||||
String args = getArgsFromIncomming(raw);
|
String args = getArgsFromIncomming(raw);
|
||||||
|
|
||||||
|
// Verify checksum; if it fails, ask the peer to repeat the message.
|
||||||
if (!verifyCheckBit(cmd, args, getCheckBitFromIncomming(raw).toInt()))
|
if (!verifyCheckBit(cmd, args, getCheckBitFromIncomming(raw).toInt()))
|
||||||
{
|
{
|
||||||
repeat();
|
repeat();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Send acknowledgement back for a valid message.
|
||||||
acknowledge(getCheckBitFromIncomming(raw).toInt());
|
acknowledge(getCheckBitFromIncomming(raw).toInt());
|
||||||
|
|
||||||
|
// Dispatch known commands to their handlers if present.
|
||||||
if (cmd == "CAL-BGN")
|
if (cmd == "CAL-BGN")
|
||||||
{
|
{
|
||||||
if (calibrationBeginHandler)
|
if (calibrationBeginHandler)
|
||||||
@@ -42,6 +51,7 @@ void SerialConnector::cycle()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Unknown/unused command - intentionally ignored for now.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -58,27 +68,33 @@ String SerialConnector::stringToHex(String cmd)
|
|||||||
|
|
||||||
for (const auto &item : cmd)
|
for (const auto &item : cmd)
|
||||||
{
|
{
|
||||||
|
// Convert character to its ASCII code then to hexadecimal digits.
|
||||||
hex_dec = int(item);
|
hex_dec = int(item);
|
||||||
|
|
||||||
|
// Temporary buffer to hold the hex digits in reverse order.
|
||||||
|
// Size 100 is intentionally generous for safety; characters only need 2 digits.
|
||||||
char hexaDeciNum[100];
|
char hexaDeciNum[100];
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
|
// Extract hex digits (least significant first).
|
||||||
while (hex_dec != 0)
|
while (hex_dec != 0)
|
||||||
{
|
{
|
||||||
int temp = 0;
|
int temp = 0;
|
||||||
temp = hex_dec % 16;
|
temp = hex_dec % 16;
|
||||||
if (temp < 10)
|
if (temp < 10)
|
||||||
{
|
{
|
||||||
hexaDeciNum[i] = temp + 48;
|
hexaDeciNum[i] = temp + 48; // '0'..'9'
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
hexaDeciNum[i] = temp + 55;
|
hexaDeciNum[i] = temp + 55; // 'A'..'F'
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
hex_dec = hex_dec / 16;
|
hex_dec = hex_dec / 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Append the digits in the correct order (reverse the temporary buffer).
|
||||||
for (int j = i - 1; j >= 0; j--)
|
for (int j = i - 1; j >= 0; j--)
|
||||||
{
|
{
|
||||||
output += hexaDeciNum[j];
|
output += hexaDeciNum[j];
|
||||||
@@ -100,27 +116,31 @@ int SerialConnector::stringToCheckNum(String cmd)
|
|||||||
|
|
||||||
for (const auto &item : cmd)
|
for (const auto &item : cmd)
|
||||||
{
|
{
|
||||||
|
// Build a numeric checksum by converting each character to a set of
|
||||||
|
// hexadecimal digits and accumulating them into an integer.
|
||||||
hex_dec = int(item);
|
hex_dec = int(item);
|
||||||
char hexaDeciNum[100];
|
char hexaDeciNum[100];
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
|
// Convert ASCII code to hex digits (LSB-first in buffer).
|
||||||
while (hex_dec != 0)
|
while (hex_dec != 0)
|
||||||
{
|
{
|
||||||
int temp = 0;
|
int temp = 0;
|
||||||
temp = hex_dec % 16;
|
temp = hex_dec % 16;
|
||||||
if (temp < 10)
|
if (temp < 10)
|
||||||
{
|
{
|
||||||
hexaDeciNum[i] = temp + 48;
|
hexaDeciNum[i] = temp + 48; // numeric digit
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
hexaDeciNum[i] = temp + 55;
|
hexaDeciNum[i] = temp + 55; // alphabetic hex digit
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
hex_dec = hex_dec / 16;
|
hex_dec = hex_dec / 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Accumulate digits into the output integer (reverse order to correct endianness).
|
||||||
for (int j = i - 1; j >= 0; j--)
|
for (int j = i - 1; j >= 0; j--)
|
||||||
{
|
{
|
||||||
output += hexaDeciNum[j];
|
output += hexaDeciNum[j];
|
||||||
@@ -138,12 +158,15 @@ int SerialConnector::stringToCheckNum(String cmd)
|
|||||||
*/
|
*/
|
||||||
int SerialConnector::getCharIndex(char ch, String str)
|
int SerialConnector::getCharIndex(char ch, String str)
|
||||||
{
|
{
|
||||||
|
// Linear search for the first occurrence of ch.
|
||||||
|
// Cache length in an int to avoid signed/unsigned comparison warnings.
|
||||||
for (int i = 0; i < str.length(); i++)
|
for (int i = 0; i < str.length(); i++)
|
||||||
{
|
{
|
||||||
if (str.charAt(i) == ch)
|
if (str.charAt(i) == ch)
|
||||||
return i;
|
return i; // return index on first match
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Not found
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,6 +178,7 @@ int SerialConnector::getCharIndex(char ch, String str)
|
|||||||
*/
|
*/
|
||||||
String SerialConnector::getCommandFromIncomming(String incomming)
|
String SerialConnector::getCommandFromIncomming(String incomming)
|
||||||
{
|
{
|
||||||
|
// Find position of first separator '#' and return everything before it.
|
||||||
int cmdEnd = getCharIndex('#', incomming);
|
int cmdEnd = getCharIndex('#', incomming);
|
||||||
return incomming.substring(0, cmdEnd);
|
return incomming.substring(0, cmdEnd);
|
||||||
}
|
}
|
||||||
@@ -166,11 +190,14 @@ String SerialConnector::getCommandFromIncomming(String incomming)
|
|||||||
*/
|
*/
|
||||||
String SerialConnector::getArgsFromIncomming(String incomming)
|
String SerialConnector::getArgsFromIncomming(String incomming)
|
||||||
{
|
{
|
||||||
|
// Extract arguments between the first and second '#' separators.
|
||||||
int cmdEnd = getCharIndex('#', incomming);
|
int cmdEnd = getCharIndex('#', incomming);
|
||||||
String cm = incomming.substring(0, cmdEnd);
|
|
||||||
|
|
||||||
|
// substring containing args and checksum
|
||||||
String afterCmd = incomming.substring(cmdEnd + 1, incomming.length());
|
String afterCmd = incomming.substring(cmdEnd + 1, incomming.length());
|
||||||
int cmdArgsEnd = getCharIndex('#', afterCmd);
|
int cmdArgsEnd = getCharIndex('#', afterCmd);
|
||||||
|
|
||||||
|
// return args portion (may be empty)
|
||||||
return afterCmd.substring(0, cmdArgsEnd);
|
return afterCmd.substring(0, cmdArgsEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,11 +208,13 @@ String SerialConnector::getArgsFromIncomming(String incomming)
|
|||||||
*/
|
*/
|
||||||
String SerialConnector::getCheckBitFromIncomming(String incomming)
|
String SerialConnector::getCheckBitFromIncomming(String incomming)
|
||||||
{
|
{
|
||||||
|
// Extract checksum string which follows the second '#' separator.
|
||||||
int cmdEnd = getCharIndex('#', incomming);
|
int cmdEnd = getCharIndex('#', incomming);
|
||||||
String cm = incomming.substring(0, cmdEnd);
|
|
||||||
|
|
||||||
String afterCmd = incomming.substring(cmdEnd + 1, incomming.length());
|
String afterCmd = incomming.substring(cmdEnd + 1, incomming.length());
|
||||||
int cmdArgsEnd = getCharIndex('#', afterCmd);
|
int cmdArgsEnd = getCharIndex('#', afterCmd);
|
||||||
|
|
||||||
|
// substring from after the second '#' to the end contains the checksum
|
||||||
return afterCmd.substring(cmdArgsEnd + 1, afterCmd.length());
|
return afterCmd.substring(cmdArgsEnd + 1, afterCmd.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,8 +227,10 @@ String SerialConnector::getCheckBitFromIncomming(String incomming)
|
|||||||
*/
|
*/
|
||||||
bool SerialConnector::verifyCheckBit(String cmd, String args, int checkBit)
|
bool SerialConnector::verifyCheckBit(String cmd, String args, int checkBit)
|
||||||
{
|
{
|
||||||
|
// Compose the portion of the message that should have been used to compute the checksum
|
||||||
String toCheck = cmd + "#" + args;
|
String toCheck = cmd + "#" + args;
|
||||||
|
|
||||||
|
// Compare computed checksum against the provided value
|
||||||
if (stringToCheckNum(toCheck) == checkBit)
|
if (stringToCheckNum(toCheck) == checkBit)
|
||||||
return true;
|
return true;
|
||||||
else
|
else
|
||||||
@@ -212,6 +243,8 @@ bool SerialConnector::verifyCheckBit(String cmd, String args, int checkBit)
|
|||||||
*/
|
*/
|
||||||
void SerialConnector::repeat()
|
void SerialConnector::repeat()
|
||||||
{
|
{
|
||||||
|
// Send a repeat request. Protocol: RPT##<code>@
|
||||||
|
// The hard-coded value '410' represents an application-specific code.
|
||||||
String cmd = "RPT##410@";
|
String cmd = "RPT##410@";
|
||||||
Serial.print(cmd);
|
Serial.print(cmd);
|
||||||
}
|
}
|
||||||
@@ -222,8 +255,12 @@ void SerialConnector::repeat()
|
|||||||
*/
|
*/
|
||||||
void SerialConnector::acknowledge(int checkBit)
|
void SerialConnector::acknowledge(int checkBit)
|
||||||
{
|
{
|
||||||
|
// Build an acknowledgement message that includes the original check bit
|
||||||
|
// so the sender can verify the ack corresponds to the correct message.
|
||||||
String cmd = "ACKG";
|
String cmd = "ACKG";
|
||||||
String toSend = cmd + "#" + (String)checkBit;
|
String toSend = cmd + "#" + (String)checkBit;
|
||||||
|
|
||||||
|
// Compute checksum for the acknowledgement itself and append terminator.
|
||||||
int checkNum = stringToCheckNum(toSend);
|
int checkNum = stringToCheckNum(toSend);
|
||||||
String final = toSend + "#" + (String)checkNum + "@";
|
String final = toSend + "#" + (String)checkNum + "@";
|
||||||
Serial.print(final);
|
Serial.print(final);
|
||||||
@@ -237,11 +274,13 @@ void SerialConnector::acknowledge(int checkBit)
|
|||||||
*/
|
*/
|
||||||
void SerialConnector::sendCommand(String cmd, String args)
|
void SerialConnector::sendCommand(String cmd, String args)
|
||||||
{
|
{
|
||||||
|
// Compose message with args and computed checksum, then send over Serial.
|
||||||
String toSend = cmd + "#" + args;
|
String toSend = cmd + "#" + args;
|
||||||
int checkNum = stringToCheckNum(toSend);
|
int checkNum = stringToCheckNum(toSend);
|
||||||
String final = toSend + "#" + (String)checkNum + "@";
|
String final = toSend + "#" + (String)checkNum + "@";
|
||||||
Serial.print(final);
|
Serial.print(final);
|
||||||
|
|
||||||
|
// Wait for and handle the peer's response (ACKG or RPT).
|
||||||
afterSendCheck(final);
|
afterSendCheck(final);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,37 +290,46 @@ void SerialConnector::sendCommand(String cmd, String args)
|
|||||||
*/
|
*/
|
||||||
void SerialConnector::afterSendCheck(String cmd)
|
void SerialConnector::afterSendCheck(String cmd)
|
||||||
{
|
{
|
||||||
|
// Block until a response terminator '@' is received and read the raw message.
|
||||||
String raw = Serial.readStringUntil('@');
|
String raw = Serial.readStringUntil('@');
|
||||||
|
|
||||||
|
// If nothing was read, keep waiting (simple recursive wait). This is a blocking behavior.
|
||||||
if (raw == "")
|
if (raw == "")
|
||||||
afterSendCheck(cmd);
|
afterSendCheck(cmd);
|
||||||
|
|
||||||
|
// Determine the type of response (RPT = repeat request, ACKG = acknowledgement).
|
||||||
String comm = getCommandFromIncomming(raw);
|
String comm = getCommandFromIncomming(raw);
|
||||||
|
|
||||||
if (comm == "RPT")
|
if (comm == "RPT")
|
||||||
{
|
{
|
||||||
|
// If peer requested a repeat, resend the original command (reconstruct from cmd string).
|
||||||
sendCommand(getCommandFromIncomming(cmd), getArgsFromIncomming(cmd));
|
sendCommand(getCommandFromIncomming(cmd), getArgsFromIncomming(cmd));
|
||||||
}
|
}
|
||||||
else if (comm == "ACKG")
|
else if (comm == "ACKG")
|
||||||
{
|
{
|
||||||
|
// Validate the acknowledgement corresponds to the command we sent.
|
||||||
int check = getArgsFromIncomming(raw).toInt();
|
int check = getArgsFromIncomming(raw).toInt();
|
||||||
int cmdCheck = getCheckBitFromIncomming(cmd).toInt();
|
int cmdCheck = getCheckBitFromIncomming(cmd).toInt();
|
||||||
|
|
||||||
if (check != cmdCheck)
|
if (check != cmdCheck)
|
||||||
{
|
{
|
||||||
// TODO: Implement a command to resend previous command
|
// Checksums do not match - TODO: implement resend logic or error reporting.
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// Acknowledgement matches - nothing further to do for now.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SerialConnector::onCalibrationBegin(void (*handler)(String args))
|
void SerialConnector::onCalibrationBegin(void (*handler)(String args))
|
||||||
{
|
{
|
||||||
|
// Register the callback invoked when a CAL-BGN message is received.
|
||||||
calibrationBeginHandler = handler;
|
calibrationBeginHandler = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SerialConnector::onCalibrationInterupt(void (*handler)(String args))
|
void SerialConnector::onCalibrationInterupt(void (*handler)(String args))
|
||||||
{
|
{
|
||||||
|
// Register the callback invoked when a CAL-INT message is received.
|
||||||
calibrationInteruptHandler = handler;
|
calibrationInteruptHandler = handler;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user