simple serial protocol

Dependents:   AwsomeStation LoRaBaseStation LoRaTerminal

SerialInterfaceProtocol.cpp

Committer:
rba90
Date:
2016-09-01
Revision:
10:2a710d0bab2c
Parent:
9:61ed835bb206
Child:
11:390476907bfc

File content as of revision 10:2a710d0bab2c:

#include "SerialInterfaceProtocol.h"
#include "mbed.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 = CommandPacket::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)
{
    
    // execute the command if it's already been registered
    if (CommandVectorTable[PacketBuffer.command] != NULL)
    {       
        return CommandVectorTable[PacketBuffer.command](
            PacketBuffer.payload, 
            PacketBuffer.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) PacketBuffer.errno & 0x0F), 
            response, 
            response_length
        );
    }
    else
    {
        respond(
            0xE0 | ((uint8_t) PacketBuffer.errno & 0x0F), 
            response, 
            response_length
        );
    }
    
    return 0;   
}

int SerialInterfaceProtocol::respond(uint8_t command, uint8_t *response, uint8_t response_length)
{
    // prepare for packet buffer
    PacketBuffer.sflag = CommandPacket::CP_SFLAG;
    
    // return flag as specified by the user
    PacketBuffer.command = command;
    
    // copy buffer length
    PacketBuffer.length = response_length;
    
    // copy buffer to payload
    memcpy(PacketBuffer.payload, response, response_length);
    
    // generate checksum
    PacketBuffer.checksum = PacketBuffer.generate_checksum();
    
    // end flag
    PacketBuffer.eflag = CommandPacket::CP_EFLAG;
    
    // serialize the packet
    int total_len = PacketBuffer.length * 2 + 8;
    uint8_t buffer[total_len];
    memset(buffer, 0x0, total_len);
    
    PacketBuffer.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;
    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 == CommandPacket::CP_SFLAG)
        {
            state = CommandPacket::SFLAG;
            
            // reset variable
            payload_counter = 0;
            memset(PacketBuffer.payload, 0x0, sizeof(PacketBuffer.payload));
            PacketBuffer.errno = CommandPacket::NO_ERROR;
        }
        
        switch (state)
        {
            case CommandPacket::SFLAG:
                PacketBuffer.sflag = ch;
                state = CommandPacket::COMMAND_H;
#ifdef DEBUG_SIP
                printf("CommandPacket::SFLAG: 0x%x\r\n", PacketBuffer.sflag);
#endif
                break;
                
            case CommandPacket::COMMAND_H:
                PacketBuffer.command = hexchar_to_uint8(ch) << 4;
                state = CommandPacket::COMMAND_L;
#ifdef DEBUG_SIP
                printf("CommandPacket::COMMAND_H: 0x%x\r\n", PacketBuffer.command);
#endif
                break;
                
            case CommandPacket::COMMAND_L:
                PacketBuffer.command |= (hexchar_to_uint8(ch) & 0x0f);
                state = CommandPacket::LENGTH_H;
#ifdef DEBUG_SIP
                printf("CommandPacket::COMMAND_L: 0x%x\r\n", PacketBuffer.command);
#endif
                break;
                
            case CommandPacket::LENGTH_H:
                PacketBuffer.length = hexchar_to_uint8(ch) << 4;
                state = CommandPacket::LENGTH_L;
#ifdef DEBUG_SIP
                printf("CommandPacket::LENGTH_H: 0x%x\r\n", PacketBuffer.length);
#endif
                break;
                
            case CommandPacket::LENGTH_L:
                PacketBuffer.length |= (hexchar_to_uint8(ch) & 0x0f);
                if (PacketBuffer.length != 0) // if the length is not zero, then proceed to payload state
                {
                    state = CommandPacket::PAYLOAD_H;
                }
                else // otherwise proceed to checksum state
                {
                    state = CommandPacket::CHECKSUM_H;
                }
#ifdef DEBUG_SIP
                printf("CommandPacket::LENGTH_L: 0x%x\r\n", PacketBuffer.length);
#endif
                break;
            
            case CommandPacket::PAYLOAD_H:
                PacketBuffer.payload[payload_counter] = hexchar_to_uint8(ch) << 4; // store higher 4 bits of payload
                state = CommandPacket::PAYLOAD_L;
#ifdef DEBUG_SIP
                printf("CommandPacket::PAYLOAD_H: 0x%x\r\n", PacketBuffer.payload[payload_counter]);
#endif
                break;
                
            case CommandPacket::PAYLOAD_L:
                PacketBuffer.payload[payload_counter++] |= (hexchar_to_uint8(ch) & 0x0f); // store lower 4 bits of payload
                if (payload_counter < PacketBuffer.length) // append ch to payload until reach the length
                {
                    state = CommandPacket::PAYLOAD_H;
                }
                else
                {
                    state = CommandPacket::CHECKSUM_H;
                }
#ifdef DEBUG_SIP
                printf("CommandPacket::PAYLOAD_L: 0x%x\r\n", PacketBuffer.payload[payload_counter - 1]);
#endif
                break;
                
            case CommandPacket::CHECKSUM_H:
                PacketBuffer.checksum = hexchar_to_uint8(ch) << 4;
                state = CommandPacket::CHECKSUM_L;
#ifdef DEBUG_SIP
                printf("CommandPacket::CHECKSUM_H: 0x%x\r\n", PacketBuffer.checksum);
#endif
                break;
                
            case CommandPacket::CHECKSUM_L:
                PacketBuffer.checksum |= (hexchar_to_uint8(ch) & 0x0f);
                
                // checksum can be turned off
                if (isChecksumEnabled)
                {
                    if (PacketBuffer.verify()) // checksum match
                    {
                        state = CommandPacket::EFLAG;
                    }
                    else // checksum mismatch
                    {
                        // clear response and response length
                        response_length = 0;
                        memset(response, 0x0, sizeof(response));
                        
                        // prepare for checksum error response
                        PacketBuffer.errno = CommandPacket::INVALID_CS_ERROR;
                        assemble(response, response_length);
                        
                        state = CommandPacket::NONE;
                    }
                }
                else
                {
                    state = CommandPacket::EFLAG;
                }
                
                
#ifdef DEBUG_SIP
                printf("CommandPacket::CHECKSUM_L: 0x%x\r\n", PacketBuffer.checksum);
#endif
                break;
                
            case CommandPacket::EFLAG:
                if (ch == CommandPacket::CP_EFLAG)
                {
                    PacketBuffer.eflag = 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
                    {
                        PacketBuffer.errno = CommandPacket::INVALID_CMD_ERROR;
                    }
                    else if (ret != 0) // error to execute
                    {
                        PacketBuffer.errno = CommandPacket::INVALID_EXEC_ERROR;
                    }
                    else
                    {
                        PacketBuffer.errno = CommandPacket::NO_ERROR;
                    }
                    
                    assemble(response, response_length);
                }
                state = CommandPacket::NONE;
#ifdef DEBUG_SIP
                printf("CommandPacket::EFLAG: 0x%x\r\n", PacketBuffer.eflag);
#endif
                break;
                
            case CommandPacket::NONE:
                // clear response and response length
                response_length = 0;
                memset(response, 0x0, sizeof(response));
                
                // Execute error generator
                PacketBuffer.errno = CommandPacket::INVALID_SFLAG_ERROR;
                assemble(response, response_length);
                
#ifdef DEBUG_SIP
                printf("CommandPacket::NONE\r\n");
#endif
                break;
                
            default:
                break;
        }
    }
}

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

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