Reading data from multiple UART devices

As my project evolves, I am discovering that the 2K or RAM on the 328 is not enough for my codes needs.

My project involves reading data from two separate UART systems (a UART from a car stereo system and a UART from an audio bluetooth module) and act like a translator. The complication with this is that it is possible for both systems to be transmitting at the same time.

I am using the hardware UART to communicate with the car stereo at 19200bps, and a software UART to communicate with the bluetooth module at 9600bps. When a command comes from the car stereo (triggered by user input to play, pause, or change tracks), a subroutine is called that captures the command. The command length is variable, but contains the number of bytes in the command, so I know how many bytes I need to read to capture the entire command. The captured command is then decoded. If the command is for the device connected to the bluetooth module, the appropriate bluetooth command is sent.

The bluetooth module may respond with metadata (track name, artist, album, length), depending on bluetooth command send. The response can contain anywhere from 200 to 512 bytes.

The problem is that data may be send from both systems at the same time. Also, the car stereo may send multiple commands at once.
The commands are of the format 0xFF 0x55 (header) (command size 1 byte) (command mode 1 byte) (command 2 bytes) (parameters 0-n bytes) (checksum 1 byte)
Below is the logic to read and respond to one or multiple commands:

void loop()
{
  //process command if we have more than 6 bytes available
 // the shortest command from the car stereo is 7 bytes
  if(Serial.available() > 6) parseCommand();
}

void parseCommand()
{
  //find start of the command. ignore bytes in the beginning that are not part of the header 
  while(!((Serial.read() == 0xFF) && (Serial.peek() == 0x55)))
  {
    if(!Serial.available()) return;
  }
  //At this point, we found 0xFF55
  Serial.read(); //pull the 0x55 out of the Rx buffer
  uint8_t i = 0;

  //The next byte is the cmd length. Make sure its there before reading.
  serialWaitForBytes(1);
  uint8_t cmdLength = Serial.read();
  
  //if the length is 0x00, its a data transfer we dont care about
  //stall for the duration of the transmission and send ack
  if(cmdLength == 0x00) //data transfer
  {
    serialWaitForBytes(2);
    uint16_t dataSize = Serial.read() << 8;
    dataSize |= Serial.read();   
    //delay(300);
    delay((dataSize*.5)+50);
    Serial.write(SucessUploadPic, sizeof(SucessUploadPic)); //ack
    serialClearRxBuffer();
    //wait for next command
    return;
  }

  //Not a data transfer. Get the mode and cmd parameters.
  serialWaitForBytes(3);
  uint8_t lenParams = cmdLength-3;
  uint8_t mode = Serial.read();      //read one byte mode value
  uint16_t cmd = Serial.read() << 8; //read first byte of command
  cmd |= Serial.read();              //read second byte of command
  
  //If it s a picrure block command, ignore it. Same logic as data transfers.
  //wait for transfer period and send ack
  if(cmd == 0x0032)               // Picture Block
  {
    delay((cmdLength*.5)+1);
    //We dont have a screen, so lets just say it was successfull...
    Serial.write(SucessUploadPic, sizeof(SucessUploadPic));
    serialClearRxBuffer();
    //wait for next command
    return;
  }
 
  //If we are here, we have a command with 0-n parameters that we need to decode
  uint16_t paramSum = 0x00;
  uint8_t* paramBuffer = 0x00;  
  //If there are any params in this command, make enough room to store them and read them
  if((lenParams) != 0)                  
  {
    paramBuffer = new uint8_t[lenParams]; 
    for(i=0;i<lenParams;++i)
    {
      serialWaitForBytes(1);
      paramBuffer[i] = Serial.read();
      //sum up all the parameter values for the checksum calculation
      paramSum += paramBuffer[i];
    }
  }      

  //read the checksum byte
  serialWaitForBytes(1);
  uint8_t ckSum = Serial.read();
  
  //ensure that the calculated checksum matches the one read!    
  if(calcCkSum(cmdLength+mode+cmd+paramSum) != ckSum)
  {
    if(paramBuffer) delete paramBuffer;
    return;
  }

  //Decode command and respond
  decodeCommand(mode, cmd, paramBuffer, lenParams); 
  if(paramBuffer) delete paramBuffer;
}

Once a cmd is processed and the parseCommand() function returns, it immediately checks to see if there is another command to process. If there is too much of a delay in between calls to parseCommand() function, bytes are overwritten in the hardware Rx buffer. If there is a bluetooth response to a command being processes. The response would need to be processed immediately, otherwise the serial Rx buffer would be overwritten. This time delay could cause data to be overwritten in the software Rx buffer as parseCommand() would be delayed.

void loop()
{
  //process command if we have more than 6 bytes available
 // the shortest command from the car stereo is 7 bytes
  if(Serial.available() > 6) parseCommand();

  //check the status of metadata response buffer
  //getMetadata() will have to read up to 512 bytes at a transfer rate of 9600bps
  if(swSerial.available() > 10) getMetadata();
}

I dont see any way around this. The Rx buffers are only 64 bytes. My only thought is to increase the Rx buffers of the hardware and software serial objects to a size that could contain an entire message from the car and the bluetooth module. Since each Rx buffer is populated via an interrupt routine, they could be populated at the same time in the background and be read and processed without worrying about data being overwritten. The issue with this is that with only 2K of RAM, I would most certainly run out of memory during run time.

My other thought was to move to a platform with more memory. I was looking at the Teensy 3.1 USB Development Board from PJRC Store. It has multiple hardware UARTS and a incredible 64K of RAM and 3 hardware UARTs. I could up the UART Rx buffer and not have to worry about reading from the buffers before the data is overwritten. It is also compatible with the Arduino libraries so I would not need to change my code.

Is this my only viable option? Or is there some way to make this work with the 2K limit of the 328? I know I can use a Mega, but I want to keep the PCB small.

This could solve both your needs - dual hardware serial ports, 16K SRAM:
http://www.crossroadsfencing.com/BobuinoRev17/
I am putting in parts orders now for another 25 kits.

I would ditch the 328 and get a board with at least 2 UARTs and plenty of RAM, for $20-30 why muck around.

There are many options, Bob's board and the Teensy are both quite suitable I would say. 3 UARTs might be nice though so you can debug at the same time.


Rob

Graynomad:
I would ditch the 328 and get a board with at least 2 UARTs and plenty of RAM, for $20-30 why muck around.

This is the conclusion I am coming to. I like the compactness of the Teensy 3.1, as well as the massive amount of resources. Also, having a dedicated UART for debugging would be handy.

Hmmm...

Are there any known compatibility issues with the Teensy3.1 and the Arduino libraries??

I don't know, maybe on Paul's site he lists any incompatibilities, although as a glance I can't see any such list.


Rob