8
$\begingroup$

A common scenario is to have a PC that sends commands to a microcontroller via RS232. My PC program is sending commands (each of which are composed of multiple bytes) as fast as it can to a small robot. The microcontroller on the robot is a Parallax Propellor.

I have noticed that if I don't process bytes quickly enough on the microcontroller side of things, it can very quickly overflow the default buffers in the popular serial port drivers that are available for the Propellor. (The buffers are generally anywhere from 16 to 256 bytes). I can arbitrarily increase these buffers or create my own larger circular buffer, but I would like to have a more methodical approach to determining appropriate size requirements and/or the minimal amount of time I can wait before pulling bytes out of the serial port driver buffer.

At 1st glance:

  • 115200 == 115.2 bits per millisecond == ~12.8 bytes per millisecond (assuming 1 stop bit)

1) Is that a valid way to calculate timing for serial transmissions?

Also, given my specific setup:

  • PC Program <--> Bluetooth Serial Profile Driver <--> Bluetooth Transceiver <-*-> BlueSMIRF Wireless Modem <--> Parallax Propellor Program

2) What is the maximum amount of data I can send for a given period of time consistently without eventually running in to problems?

Maybe I'm over complicating things, but it seems like there are potentially multiple buffers involved in the transmission chain above. How do others commonly deal with this? Do they throttle the PC sending to a known safe rate? Implement flow control? If implementing flow control, how does that affect bandwidth and response times?

(If it matters, my experiment is to use a joystick on the PC to control multiple servos with instant reaction to the joystick movements. So every small movement of the joystick results in multiple commands being sent to the microcontroller. The commands are not just simple positional commands though, they also involve acceleration/deacceleration of servos over time and this is the reason that the microcontroller spends a significant amount of clock cycles before processing new bytes.)

$\endgroup$

5 Answers 5

2
$\begingroup$

I think the form of your question is wrong. The problem is not that you've improperly calculated how much data-per-second can be thrown at the microcontroller; it's that you have no way for the microcontroller to indicate its readiness to receive the next command.

To put it another way, if you attempt to solve this problem by precisely calculating how quickly to send data, you will inevitably do one of the following things:

  1. send less data than the microcontroller can handle
  2. send more data than the microcontroller can handle (and in the worst case, it will be only very slightly more so that the buffer takes an hour to overrun and leads you to debug something completely unrelated)

The solution is for your microcontroller to provide feedback -- flow control. In the most basic example just send back a single character representing command completion (@Rocketmagnet's SpaceWire suggestion would be the most robust, but also heavy handed).

Most likely, you can afford to have a few commands in the buffer. So, the calculation you should do is to divide your buffer size by the size of the largest command you'll send, then subtract 1 (or more) for safety. That will tell you the greatest allowable difference between commands sent and acknowledgements received, and will allow you to work at maximum speed by keeping the buffer filled.

Example

Say that your longest possible command is 7 bytes ("fw 4.32") or some such, and that you have a 64-byte buffer. That means you can fit 9 commands in the buffer ($7 \times 9 = 63 < 64$), but for safety you'll subtract one and only allow 8 commands. Here's some python psudeocode:

MAX_UNACKED = 8  # constant, how many un-acked commands to send
unacked = 0      # number of un-acked commands that have been sent
while (true):
    # can send up to MAX_UNACKED commands as we like
    if MAX_UNACKED > unacked:
        send_command()
        unacked = unacked + 1

    # assume that the ack is a single character, we don't care which
    acks = receive_response()      # receive a string of characters
    unacked = unacked - len(acks)  # number of characters = number of acks

Note that this psuedocode is an example of spin blocking and that there are better ways to wait for input... but which one specifically will depend on how your PC code works.

$\endgroup$
2
  • $\begingroup$ Thanks, this is a great answer. However, the delay of waiting for an "ack" type message is quite significant. I was hoping to avoid it, but perhaps it is simply unavoidable for 100% reliability. I agree the Spacewire protocol suggested by Rocketmagnet looks very robust, but perhaps too much for what I need. $\endgroup$
    – kaliatech
    Nov 12, 2012 at 22:37
  • $\begingroup$ Well, remember that you don't need to wait for each command to be ack'd. You can send as many commands as you have buffer space, and then start sending one message per ack received. That should eliminate any delays (by keeping the microcontroller command queue full) but prevent you from overrunning the buffer -- the original problem. $\endgroup$
    – Ian
    Nov 13, 2012 at 14:23
4
$\begingroup$

I'd suggest two possible approaches.

  1. Use a 'heartbeat' to transfer a well known state packet at a fixed frequency that fits within your 'speed budget'. In this case, you never send ad hoc messages directly from the PC or MCU, all you can do is update the state packet which will be sent at the scheduled time.

  2. Put a hard limit on your 'transmission budget' and enforce it. E.g. you can only send one packet every 100 ms and if a 2nd packet is submitted before 100 ms has expired, its transmission is delayed until the 100 ms timeout (e.g. quantum) has expired. This requires you to add messages to a queue and then send from the queue at a fixed rate. Similar in approach to the heartbeat in #1 but a bit more efficient since data isn't sent when it hasn't changed. The downside of this design is if you are oversending, you build higher and higher latencies into your communications and if you have variable bursty communication the communications latency can vary widely.

I tend to work with #1 to send data from the MCU since the algorithm is simple and small.

$\endgroup$
3
$\begingroup$

There is no right answer to this, and I'm sure you already know (or can guess) all you need to solve this problem. However

The first obvious thing to say is that downstream devices must be capable of dealing with the flow of data, both in the long term and in the short term. In the short term, devices use buffering to cope with the flow. In the long term, the need processing power to act on the data.

One problem you have is that you don't control all of the steps in your chain, so if one of them is causing delays, there might not be much you can do about it, short of replacing it. Hoever, I'd have thought that the Bluetooth serial driver would have a nice big buffer, and would play nice with the transceiver and BlueSMIRF, so it's probably safe to ignore them. I guess the real problem is between the Propeller and the PC.

What seems to be happening in your case is that the interaction between the data producer and the consumer is producing unpredictable queue lengths, and you would like to apply some proper theory to that.

Interestingly, studies have been done on exactly this problem with regards to queuing at restaurants, E.G. Case Study for Restaurant Queuing Model and Equilibrium in Queues under Unknown Service Times and Service Value. But there are easier to understand resources online, like Queueing Theory Queueing Theory for Dummies. But the one you really want is probably How to size message queues.

You have a few options:

  • Just make sure that the PC sends data at an orderly rate. Decide exactly what rate you really need, and don't go above that.
  • Use a Realtime Operating System on the MCU to make sure that the bytes are being dealt with in a timely manner.
  • Implement flow control.

But here's my preferred solution. Spacewire! Or at least use the flow control system from it.

Essentially the downstream device sends bytes to the upstream device stating the number of empty places in its FIFO. This way the data producer only sends what the consumer can cope with.

If you're interested, you can read the full Spacewire standard: IEEE 1355.

$\endgroup$
2
$\begingroup$

At 1st glance:

115200 == 115.2 bits per millisecond == ~12.8 bytes per millisecond (assuming 1 stop bit)

Is that a valid way to calculate timing for serial transmissions?

Very simplistically, this is OK - but don't forget any start and parity bits also, and don't forget any protocol overhead along the bluetooth links

Also, given my specific setup:

PC Program <--> Bluetooth Serial Profile Driver <--> Bluetooth Transceiver <-*-> BlueSMIRF Wireless Modem <--> Parallax Propellor Program

I think it is reasonable to assume that the PC through to the Modem are capable of handling the traffic at 115200 bps, so you can eliminate these from the equation.

However, having multiple hops like this does prevent use of flow-control signals, without introducing response messages... which would slow down response time.

Now, take the worst case, of no protocol overhead, 115200 bps means your Parallax will be receiving a byte every 69us - adding in start, stop or parity bits will slow this rate down a little bit, but assuming worst case gives you some leeway.

This means that your controller has to handle receiving a byte every 69us, as well as doing its normal stuff (calculations etc).

But in reality, you will be sending a message string of (n) bytes, which need to be buffered, and processed as a string - whilst still doing the normal stuff. Calculating the buffer is an art in itself, but I would normally work on (a minimum of) twice the size of the longest string (if you have the RAM space). Anything less has the potential for losing messages, if one is not processed before reception of the next commences.

If you are restricted to only the length of the longest message, you need to be 100% sure that you can process that message between the receipt of the last byte, and the receipt of the first byte of the next message. (Obviously less of a problem with smaller messages).

$\endgroup$
0
$\begingroup$

Instead of calculating the speed requirement (which may vary from time to time/comp to comp and whenever you change the code), there are some other things you can do:

  • Have your μC request input by sending interrupts to the master device (in this case, it's a PC--you may not need to use interrupts, just keep a port reserved). You can either have the master device buffer the outputs (easier to do that with a pc--effectively unlimited buffering), or you can just discard them unless necessary.

  • With your scenario, it's better to get rid of buffering entirely. Accept the inputs to the μC via the interrupt pin. Have some flag variable that basically says "need input" on the μC. If this flag is set, the μC will accept the input. Otherwise, it will just discard it. This way, all the input is rapidly consumed (keeping buffers free), but not all is used. For efficiency, adjust the timing to the approximate baud of the entire setup. Instead of discarding it, you may wish to code your own buffer-type thing--have a small array in which a few inputs are stored. Now, you have gotten rid of all the upstream buffers, and have a single "artificial" buffer on the μC--over which you have complete control.

  • It may be better to keep all computations on the pc and use the μC as a simple slave controller--give it simple motion/position commands that can be piped directly to the motors. If you have enough interrupt pins/interrupt MUXing, you may want to send these commands via interrupts for faster response.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.