simple serial protocol

Dependents:   AwsomeStation LoRaBaseStation LoRaTerminal

SerialInterfaceProtocol.cpp

Committer:
rba90
Date:
2016-09-02
Revision:
11:390476907bfc
Parent:
10:2a710d0bab2c

File content as of revision 11:390476907bfc:

#include "mbed.h"
#include "SerialInterfaceProtocol.h"
#include "CommandPacket2.h"

SerialInterfaceProtocol::SerialInterfaceProtocol(SerialBuffer_t *in, SerialBuffer_t *out)
{
    // assign input and output buffers
    SerialInputBuffer = in;
    SerialOutputBuffer = out;
    
    // init command vector table
    for (int i = 0; i < SIP_CMD_VECTOR_TABLE_SZ; i++)
    {
        CommandVectorTable[i] = NULL;
    }
    
    // init internal state machine
    state = NONE;
    
    // init internal state
    isChecksumEnabled = false;
}

SerialInterfaceProtocol::~SerialInterfaceProtocol()
{
    
}

void SerialInterfaceProtocol::registerCommand(uint8_t command, callback_func f)
{
    CommandVectorTable[command] = f;
}

void SerialInterfaceProtocol::deRegisterCommand(uint8_t command)
{
    CommandVectorTable[command] = NULL;
}

int SerialInterfaceProtocol::execute(uint8_t *response, uint8_t *response_length)
{
    // read command from packet buffer
    uint8_t command = PacketBuffer.getCommand();
    
    // execute the command if it's already been registered
    if (CommandVectorTable[command] != NULL)
    {
        // extract length and payload from packet buffer
        uint8_t length = PacketBuffer.getLength();
        uint8_t payload[length];
        memset(payload, 0x0, length);
        
        for (uint8_t i = 0; i < length; i++)
        {
            payload[i] = PacketBuffer.getPayload(i);
        }
        
        return CommandVectorTable[command](
            payload, 
            length,
            response, 
            response_length
        );
    }
    
    return -1;
}

int SerialInterfaceProtocol::assemble(uint8_t *response, uint8_t response_length)
{
    // if no response, return E, otherwise return F
    if (response_length != 0)
    {
        respond(
            0xD0 | ((uint8_t) errno & 0x0F), 
            response, 
            response_length
        );
    }
    else
    {
        respond(
            0xE0 | ((uint8_t) errno & 0x0F), 
            response, 
            response_length
        );
    }
    
    return 0;   
}

int SerialInterfaceProtocol::respond(uint8_t command, uint8_t *response, uint8_t response_length)
{
    // create a new packet buffer
    CommandPacket2 responsePacket;
    
    // set sflag
    responsePacket.setSFlag();  // use default sflag
    
    // return flag as specified by the user
    responsePacket.setCommand(command);
    
    // copy buffer length
    responsePacket.setLength(response_length);
    
    // copy buffer to payload
    for (uint8_t i = 0; i < response_length; i++)
    {
        responsePacket.setPayload(i, response[i]);
    }
    
    // generate checksum
    responsePacket.generateChecksum();
    
    // end flag
    responsePacket.setEFlag();
    
    // serialize the packet
    int total_len = response_length * 2 + 8;
    uint8_t buffer[total_len];
    memset(buffer, 0x0, total_len);
    
    responsePacket.serialize(buffer);
    
#ifdef DEBUG_SIP
    printf("SIP::respond:total: %d chars\r\n", total_len);
#endif

    // add to ring buffer
    for (int i = 0; i < total_len; i++)
    {
#ifdef DEBUG_SIP
        printf("SIP::respond:0x%x (%c)\r\n", buffer[i], buffer[i]);
#endif
        SerialOutputBuffer->enqueue(buffer[i]);
    }
    
    return 0;
};
    

void SerialInterfaceProtocol::poll()
{
    static uint8_t payload_counter = 0;
    
    // temp variable for building full byte from hex strings
    static uint8_t command = 0;
    static uint8_t length = 0;
    static uint8_t payload = 0;
    static uint8_t checksum = 0;
    
    uint8_t response[SIP_MAX_RESP_LEN];
    uint8_t response_length;
    
    // fetch data from ring buffer
    while (SerialInputBuffer->getCounter() > 0)
    {
        uint8_t ch;
        ch = SerialInputBuffer->dequeue();
        
        // reset state to keep sync
        if (ch == CommandPacket2::CP_SFLAG)
        {
            state = SFLAG;
            
            // reset variable
            payload_counter = 0;
            errno = NO_ERROR;
        }
        
        switch (state)
        {
            case SFLAG:
                PacketBuffer.setSFlag(ch);
                state = COMMAND_H;
#ifdef DEBUG_SIP
                printf("SIP::SFLAG: 0x%x\r\n", PacketBuffer.getSFlag());
#endif
                break;
                
            case COMMAND_H:
                command = hexchar_to_uint8(ch) << 4;
                state = COMMAND_L;
#ifdef DEBUG_SIP
                printf("SIP::COMMAND_H: 0x%x\r\n", command);
#endif
                break;
                
            case COMMAND_L:
                command |= (hexchar_to_uint8(ch) & 0x0f);
                
                // store command
                PacketBuffer.setCommand(command);
                
                state = LENGTH_H;
#ifdef DEBUG_SIP
                printf("SIP::COMMAND_L: 0x%x\r\n", command);
#endif
                break;
                
            case LENGTH_H:
                length = hexchar_to_uint8(ch) << 4;
                state = LENGTH_L;
#ifdef DEBUG_SIP
                printf("SIP::LENGTH_H: 0x%x\r\n", length);
#endif
                break;
                
            case LENGTH_L:
                length |= (hexchar_to_uint8(ch) & 0x0f);
                
                // store length
                PacketBuffer.setLength(length);
                
                if (length != 0) // if the length is not zero, then proceed to payload state
                {
                    state = PAYLOAD_H;
                }
                else // otherwise proceed to checksum state
                {
                    state = CHECKSUM_H;
                }
#ifdef DEBUG_SIP
                printf("SIP::LENGTH_L: 0x%x\r\n", length);
#endif
                break;
            
            case PAYLOAD_H:
                payload = hexchar_to_uint8(ch) << 4; // store higher 4 bits of payload
                state = PAYLOAD_L;
#ifdef DEBUG_SIP
                printf("SIP::PAYLOAD_H: 0x%x\r\n", payload);
#endif
                break;
                
            case PAYLOAD_L:
                payload |= (hexchar_to_uint8(ch) & 0x0f); // store lower 4 bits of payload
                
                // store payload
                PacketBuffer.setPayload(payload_counter++, payload);
                
                if (payload_counter < PacketBuffer.getLength()) // append ch to payload until reach the length
                {
                    state = PAYLOAD_H;
                }
                else
                {
                    state = CHECKSUM_H;
                }
#ifdef DEBUG_SIP
                printf("SIP::PAYLOAD_L: 0x%x\r\n", payload);
#endif
                break;
                
            case CHECKSUM_H:
                checksum = hexchar_to_uint8(ch) << 4;
                state = CHECKSUM_L;
#ifdef DEBUG_SIP
                printf("SIP::CHECKSUM_H: 0x%x\r\n", checksum);
#endif
                break;
                
            case CHECKSUM_L:
                checksum |= (hexchar_to_uint8(ch) & 0x0f);
                
                // store checksum
                PacketBuffer.setChecksum(checksum);
                
                // checksum can be turned off
                if (isChecksumEnabled)
                {
                    if (PacketBuffer.verify()) // checksum match
                    {
                        state = EFLAG;
                    }
                    else // checksum mismatch
                    {
                        // clear response and response length
                        response_length = 0;
                        memset(response, 0x0, sizeof(response));
                        
                        // prepare for checksum error response
                        errno = INVALID_CS_ERROR;
                        assemble(response, response_length);
                        
                        state = NONE;
                    }
                }
                else
                {
                    state = EFLAG;
                }
                
                
#ifdef DEBUG_SIP
                printf("SIP::CHECKSUM_L: 0x%x\r\n", checksum);
#endif
                break;
                
            case EFLAG:
                if (ch == CommandPacket2::CP_EFLAG)
                {
                    PacketBuffer.setEFlag(ch);
                    
                    // clear response and response length
                    response_length = 0;
                    memset(response, 0x0, sizeof(response));
                    
                    // execute command
                    int ret = execute(response, &response_length);
                    
                    if (ret < 0) // command not registered
                    {
                        errno = INVALID_CMD_ERROR;
                    }
                    else if (ret != 0) // error to execute
                    {
                        errno = INVALID_EXEC_ERROR;
                    }
                    else
                    {
                        errno = NO_ERROR;
                    }
                    
                    assemble(response, response_length);
                }
                state = NONE;
#ifdef DEBUG_SIP
                printf("SIP::EFLAG: 0x%x\r\n", PacketBuffer.getEFlag());
#endif
                break;
                
            case NONE:
                // clear response and response length
                response_length = 0;
                memset(response, 0x0, sizeof(response));
                
                // Execute error generator
                errno = INVALID_SFLAG_ERROR;
                assemble(response, response_length);
                
#ifdef DEBUG_SIP
                printf("SIP::NONE\r\n");
#endif
                break;
                
            default:
                break;
        }
    }
}

void SerialInterfaceProtocol::disableChecksum()
{
    isChecksumEnabled = false;
}

void SerialInterfaceProtocol::enableChecksum()
{
    isChecksumEnabled = true;
}

uint8_t hexchar_to_uint8(uint8_t ch)
{
    uint8_t val = 0;

    if (ch >= '0' && ch <= '9')
    {
        val = ch - '0';
    }
    else if (ch >= 'A' && ch <= 'F')
    {
        val  = ch - 'A';
        val += 10;
    }
    else if (ch >= 'a' && ch <= 'f')
    {
        val = ch - 'a';
        val += 10;
    }

    return val;
}