diff --git a/master/master/config.h b/master/master/config.h --- a/master/master/config.h +++ b/master/master/config.h @@ -22,14 +22,13 @@ // -------------------------------------------------------------------------- -// Error Codes config (led.c) +// Error Codes config (led.c, used throughout code) // -------------------------------------------------------------------------- // SD Card #define ERROR_SD_INIT 2 #define ERROR_SD_PARTITION 3 - // -------------------------------------------------------------------------- // Slave Sensors config (slavesensors.c) // -------------------------------------------------------------------------- @@ -45,6 +44,19 @@ // -------------------------------------------------------------------------- +// Command Parser config (serparser.c) +// -------------------------------------------------------------------------- + +// Maximum payload size of command +#define MAX_PAYLOAD_LEN 16 + +// Circular serial buffer size. Must be at least MAX_CMD_LEN + 5 +#define BUFFER_SIZE 32 + +// Public broadcast address +#define BROADCAST_ADDR 0 + +// -------------------------------------------------------------------------- // USART config (serial.c) // -------------------------------------------------------------------------- diff --git a/master/master/lib/serial.c b/master/master/lib/serial.c --- a/master/master/lib/serial.c +++ b/master/master/lib/serial.c @@ -14,12 +14,19 @@ #include "../config.h" #include +// NOTE: USART ISRs for charachter reception are included in serparser.c + void serial0_setup() { + PORTD &= ~(1<>8); UBRR0L = (unsigned char)USART0_BAUD_PRESCALE; + + UCSR0A |= (1< #include "../config.h" #include "serial.h" -//#define DEBUG +#include "serparser.h" + + +// Circular buffer for incoming data +uint8_t buffer[BUFFER_SIZE]; -// ************* Command Definitions *************** +// Location of parser in the buffer +uint8_t bufferParsePosition = 0; + +// Location of receive byte interrupt in the buffer +volatile uint16_t bufferDataPosition = 0; -// Incoming command buffer -uint8_t buffer[MAX_CMD_LEN+2]; +// Parser state +uint8_t parserState = STATE_RESET; +uint8_t lastParserState = STATE_RESET; -// Current buffer location -uint8_t bufferPosition = 0; +// Length of current payload data (and checksum) +int dataLength = 0; -//current length of the data transmission (and checksum) -int dataLength; +// Data and checksum of most recent transmission +char receivedPayload[MAX_PAYLOAD_LEN]; + +// Payload type ID of the sensor of most recent transmission +char receivedDataType = 0; -//data (and checksum) of most recent transmission -char data[16]; - -//ID of the sensor of most recent transmission -char sensorID; +// Checksum to be calculated and then compared to the received checksum +char checksumCalc = 0; -//checksum to be calculated and then compared to the received checksum -char checksumCalc; +// Could inline if program space available +static void setParserState(uint8_t state) +{ + lastParserState = parserState; + parserState = state; + + // If resetting, clear vars + if(state == STATE_RESET) + { + dataLength = 0; + checksumCalc = 0; + } + + // Every time we change state, we have parsed a byte + bufferParsePosition = (bufferParsePosition + 1) % BUFFER_SIZE; +} -/* Process incoming serial data */ -// TODO: Maybe make this suck up the whole buffer if there is more to be had. That would make this much more efficient. -// we could parse while(SERIAL_RX_HASBYTES && notOverTimeout), don't have it parse for more than 100ms straight or something... +// Receive data on USART +ISR(USART0_RX_vect) +{ + buffer[bufferDataPosition % BUFFER_SIZE] = UDR0; + bufferDataPosition = (bufferDataPosition + 1) % BUFFER_SIZE; +} + + +// Parse data from circular buffer int serparser_parse(void) { - char byte; - if(SERIAL_RX_HASBYTES) // Only process data if buffer has a byte + // Process first command (if any) on the circular buffer + while(bufferDataPosition != bufferParsePosition) { - - // Pull byte off of the buffer - byte = serial0_readChar(); - buffer[bufferPosition] = byte; + byte = buffer[bufferParsePosition]; - // byte checking/validation - if(bufferPosition == 0) + // Reset + if(parserState == STATE_RESET) { - if(byte == '[') // If this is a start byte, we're OK. Keep reading. + if(byte == '[') // Start of frame; keep parsing + { + serial0_sendString("PARSER: start ok\r\n"); + setParserState(STATE_GETID); + } + else // Not start of frame, reset { - bufferPosition++; + serial0_sendString("PARSER: not start, reset\r\n"); + setParserState(STATE_RESET); + return PARSERESULT_NODATA; // no valid start bit; better luck next time. run the function the next time around the main loop. } - else // Not a start byte that we're expecting. Reset to initial state. + } + + // Get destination module ID + else if(parserState == STATE_GETID) + { + if(byte == MODULE_ID) // Message intended for this module; keep parsing { - bufferPosition = 0; + serial0_sendString("SER: dest ok\r\n"); + checksumCalc += byte; + setParserState(STATE_GETDATATYPE); + } + else // Transmission is intended for another module; reset + { + serial0_sendString("SER: bad dest, reset\r\n"); + setParserState(STATE_RESET); } } - else if(bufferPosition == 1) + + // Get payload type ID + else if(parserState == STATE_GETDATATYPE) { - if(byte == MODULE_ID) //this transmission is intended for the master; continue parsing - { - bufferPosition++; - checksumCalc += byte; - dataLength = 0; //reset dataLength here to protect from bad inputs - } - else //this transmission is intended for another module; stop parsing and reset - { - bufferPosition = 0; - } - } - else if(bufferPosition == 2) - { - sensorID = byte; //store the type of data receiving - bufferPosition++; + serial0_sendString("SER: type ok\r\n"); + receivedDataType = byte; // Store the type of data receiving checksumCalc += byte; + setParserState(STATE_GETDATA); } - else if (bufferPosition == MAX_CMD_LEN) //command is too long and bad data has been recieved; reset - { - bufferPosition = 0; - } - else - { - if (byte == ']') //this is the end of the transmission + + // Get payload data + else if(parserState == STATE_GETDATA) + { + if (byte == ']') // End of frame { - bufferPosition = 0; - return 2; // got whole msg + serial0_sendString("SER: eof ok\r\n"); + if(bufferParsePosition == bufferDataPosition) + { + // We are at the end of the line. No more data to parse. + setParserState(STATE_RESET); + return PARSERESULT_PARSEOK; + } + else + { + setParserState(STATE_RESET); + // we could choose to keep parsing now, or parse the next message next loop around (better idea). + // return now so we hit it the next time around + return PARSERESULT_PARSEOK; + } } - else //still receiving data + else // Still receiving data { - data[dataLength] = byte; + serial0_sendString("SER: data ok\r\n"); + receivedPayload[dataLength] = byte; dataLength++; - bufferPosition++; checksumCalc += byte; + + // Data buffer overrun protection + if(dataLength > MAX_PAYLOAD_LEN) { + serial0_sendString("SER: buf overflow, reset\r\n"); + setParserState(STATE_RESET); + return PARSERESULT_FAIL; + } + else { + // Set state. MUST call even though state is maintained to update parse position + setParserState(STATE_GETDATA); + return PARSERESULT_STILLPARSING; + } + } } } - else - { - return 0; // serial data not available - } + return PARSERESULT_NODATA; + } \ No newline at end of file diff --git a/master/master/lib/serparser.h b/master/master/lib/serparser.h --- a/master/master/lib/serparser.h +++ b/master/master/lib/serparser.h @@ -14,6 +14,23 @@ #ifndef SERPARSER_H_ #define SERPARSER_H_ +enum parseResults +{ + PARSERESULT_FAIL = 0, + PARSERESULT_NODATA, + PARSERESULT_STILLPARSING, + PARSERESULT_PARSEOK, +}; + +// Parser states +enum parseStates +{ + STATE_RESET = 0, + STATE_GETID, + STATE_GETDATATYPE, + STATE_GETDATA +}; + // Prototypes int serparser_parse(void); diff --git a/master/master/lib/slavesensors.c b/master/master/lib/slavesensors.c --- a/master/master/lib/slavesensors.c +++ b/master/master/lib/slavesensors.c @@ -55,16 +55,20 @@ void slavesensors_setup() slaves[7] = SLAVE7_SENSORS; } -bool slavesensors_process() +bool slavesensors_process(uint8_t parseResult) { // Periodic: Every Iteration // maybe we should do this in an ISR on byte received. only problem is that this could interrupt things, // but we only care about interruption during logging and such. don't log when parsing a message? - receptionFinished = serparser_parse(); + + // not anymore: + //receptionFinished = serparser_parse(); + // now we need some way to know if a transmission finished. Probably set a flag in main from the + // return that is "hasUnhandledTransmission = true" // Finished reception of a message (one sensor data value). If not finished, send out command to get the next one - if(receptionFinished == 2) + if(parseResult == PARSERESULT_PARSEOK) { bool weFinishedTHeLastSlaveSensor = false; // ex. // if we finished the final reception for this slave, set gettingValues = false; @@ -84,6 +88,7 @@ bool slavesensors_process() return true; // Keep going... } } + // TODO: Handle errors. If we got a parse fail, then we probably need to retransmit the message. etc. else { return true; // Keep going... diff --git a/master/master/master.c b/master/master/master.c --- a/master/master/master.c +++ b/master/master/master.c @@ -29,6 +29,7 @@ #include "lib/sd/sd_raw_config.h" #include "lib/looptime.h" #include "lib/slavesensors.h" +#include "lib/serparser.h" void micro_setup() { @@ -49,11 +50,12 @@ int main(void) logger_setup(); afsk_setup(); - serial0_sendString("\r\n\r\n---------------------------------\r\nHAB Controller 1.0 - Initialized!\r\n---------------------------------\r\n\r\n"); + serial0_sendString("\r\n\r\n---------------------------------\r\n"); + serial0_sendString("HAB Controller 1.0 - Initialized!\r\n"); + serial0_sendString("---------------------------------\r\n\r\n"); led_on(POWER); - // Buffer for string operations char logbuf[32]; const char* logBufPtr = logbuf; @@ -65,6 +67,10 @@ int main(void) // If we are currently processing sensor data bool isProcessing = false; + // Result of last parser run + int parseResult = PARSERESULT_NODATA; + bool haveDataToHandle = false; + // Write CSV header to SD card logger_log("ProgramTime,LastAprsBroadcast,LastLog\n"); @@ -105,10 +111,13 @@ int main(void) lastAprsBroadcast = time_millis(); } - // keep calling processSensors until it is finished + parseResult = serparser_parse(); + + // TODO: If we receive a command, we should still process it even if we aren't isProcessing, right? + // Maybe we should isolate the received command handling ("Execution") and the issuing of requests. if(isProcessing) { - isProcessing = slavesensors_process(); + isProcessing = slavesensors_process(parseResult); } wdt_reset(); }