Serial Protocol optimisation

Dear all,

I’ve got rather stuck with a serial optimisation problem between Arduino and the Unity programming environment.

I’ve implemented VictorHurdugaci’s serial protocol found here, which uses a simple message structure like so:

Message start marker: 1 byte
Message size: 1 byte
Payload (message body): variable number of bytes (max 255)
Checksum: 1 byte

The issue I’m having is that I’m having to use a delay in order to achieve proper back and forth polling, my arduino code looks like so:

#include <SoftwareSerial.h>

#include <ArduinoSerialProtocol.h> 

// The payload that will be sent to the other device
struct SamplePayload {
    int8_t A;
    uint8_t B;
    int16_t C;
    uint16_t D;
} payload;

ArduinoSerialProtocol protocol(&Serial, (uint8_t*)&payload, sizeof(payload));
uint8_t receiveState;

void setup() 
{
    Serial.begin(57600);

    payload.A = 51;
    payload.B = 52;
    payload.C = 53;
    payload.D = 54;
    protocol.send();
}

void loop()
{
    receiveState = protocol.receive();

    if (receiveState == ProtocolState::SUCCESS)
    {
        protocol.send();  
    }
    delay(10);
}

I’m scratching my head as to what could be causing this. To be clear, the Unity code reads into a buffer until it’s happy with the message then polls the arduino for an update, however without the delay the Arduino does not start a polling loop. It should work every frame within Unity (without a poll the buffer fills in under a second) but currently it only works at 10Hz when the framerate is approx 50Hz.

Any help or pointers much appreciated!

Any help or pointers much appreciated!

Based on that snippet? Not likely.

Post ALL of your code.

Ha fair enough. here are the Send and Receive methods from the library:

#include <SerialProtocol.h>

/* Message format:
    Header        (1 byte)           - Header Byte
    DataLength   (1 byte)            - The size of the payload
    Data         (DataLength bytes)  - Actual payload
    Checksum     (1 byte)            - Message integrity check

    Total message size = (payload size + 3) bytes;
*/

#define HEADER 'S'

// The number of bytes required to read before the actual payload
// is encountered
#define BYTES_TO_PAYLOAD 2

SerialProtocol::SerialProtocol(uint8_t* payload, uint8_t payloadSize)
{
    this->payload = payload;
    this->payloadSize = payloadSize;

    inputBuffer = (uint8_t*)malloc(payloadSize);
}

uint8_t SerialProtocol::send()
{
    if (!serialAvailable())
    {
        // There is no place to send the data
        return ProtocolState::NO_SERIAL;
    }

    uint8_t checksum = payloadSize;

    // Write the header
    sendData(HEADER);
    sendData(payloadSize);

    // Write the payload
    for (int i = 0; i < payloadSize; i++)
    {
        checksum ^= *(payload + i);
        sendData(*(payload + i));
    }

    // Complete with the checksum
    sendData(checksum);

    return ProtocolState::SUCCESS;
}

// This method is blocking while there is data in the serial buffer
uint8_t SerialProtocol::receive()
{
    uint8_t state;
    uint8_t data;

    if (!serialAvailable())
    {
        // Cannot get any data
        return ProtocolState::NO_SERIAL;
    }

    while(true)
    {
        data = readData();

        if (data == -1)
        {
            return bytesRead > 0 ?
                ProtocolState::WAITING_FOR_DATA :
                ProtocolState::NO_DATA;
        }

        if (bytesRead == 0)
        {
            // Look for header
            if (data != HEADER)
            {
                return ProtocolState::NO_DATA;
            }

            ++bytesRead;
            continue;
        }

        if (bytesRead == 1)
        {
            // Look for payload size
            if (data != payloadSize)
            {
                bytesRead = 0;
                return ProtocolState::INVALID_SIZE;
            }

            // The checksum starts with the payload size
            actualChecksum = data;

            ++bytesRead;
            continue;
        }

        // The > check ensures that we don't overflow and
        // that we discard bad messages
        if (bytesRead >= (payloadSize + BYTES_TO_PAYLOAD))
        {
            // We are done with the message. Regardless of the outcome
            // only a new message can come next
            bytesRead = 0;

            // Before we can be fully done, we need the
            // checksum to match
            if (actualChecksum != data)
            {
                return ProtocolState::INVALID_CHECKSUM;
            }

            memcpy(payload, inputBuffer, payloadSize);
            return ProtocolState::SUCCESS;
        }
                
        inputBuffer[bytesRead - BYTES_TO_PAYLOAD] = data;
        actualChecksum ^= data;
        
        ++bytesRead;
    }
}

Edit: To clarify, the Unity sketch posts back a properly formatted packet with the header, size, payload and chekcsum, that works fine when a delay is introduced.

Post ALL of your code. It is not possible to assess it from the snippets, and very likely problem is elsewhere.

The fact that you are asking for help is proof that you don’t know where the problem is.

I've posted the full arduino code in the first post, and the code for the library in my second, minus the prototypes and headers - I can post them here or they can be found at the GitHub link I posted above and available at github:
victorhurdugaci/SerialProtocol

I think that's fairly succinct? the Unity code uses C# and the following:

SerialPort.Write(binaryPacket, 0, binaryPacket.Length);

from the MSDN:
MSDN Serial.Write