Help with Design: Using processor time efficiently when dealing with serial

Hi everyone,

I'm currently working on developing what is essentially a temperature controller with a few other bells and whistles attached. I have a few questions about how I should approach the code design in terms of using the microprocessor efficiently (I don't want to be blocking other things from happening while waiting around for a serial reponse). The basics of my system are as follows

TouchScreenDisplay
|
(Serial)
|
USB Slave Device -----(USB)------Arduino Mega ADK-----------------RS485Bus
|
(Serial)
|
Temp monitor

There are a few other things attached but I think this is all I need to consider at this point in time. The arduino has a few tasks that need to be done often such as PID control for a heater (which is connected to a digital pin via a motor driver) and a rather large state machine function. It also needs to be able to receive commands/queries from the display and act accordingly for, example the display could ask the arduino for some propery of the USB slave device to display on screen.

The Arduino has a lot of things it needs to do so I want to minimise the time spent waiting for things e.g. a response over USB. How should I organise my code?

My thoughts so far are to have the PID function on a timer interrupt as it is a short method and needs to be regular (i guess this means I would need to query the temp monitor just as regularly and somehow ensure I don't read and write from the buffer at the same time). I think the state machine function can just be in the main loop. But, I am unsure how to handle the requests from the display. If I receive a query, should I use Serial.Event() and have code in there to send the USB device for the query and wait for a response? This seems a bit wasteful of resources. Ideally, once the query is received from the display, my code would send the query to the usb device and continue doing normal things until a response is received on which it would act (forward to the display or do some other necessay processing). How could i achieve this??

Any other comments and thoughts would be appreciated too,

Thanks a lot for your help

It's hard to tell what your timing need is from those details, but generally, the arduino is plenty fast enough to deal with serial data and other operations without the need for interrupts. Just steer clear of delay. What are you measuring the temperature of that you need such frequent updates?

Separating the two communications accomplishes what you want.
Your main loop periodically sends a serial message to read the temperature and stores it in a variable.
The loop also checks for a touch screen request. If it is a request for temperature it immediately sends the variable value.
(there is no additional transmission and wait). It processes any other type of request.
Then it begins the loop again. The only trick is to perhaps snap the time (millis()) when you last read the temperature, so that you can compare it to the current time and not read it too quickly.
This way, no delay is used. You get instant response to your touch, except if it happens to be in the middle of a temperature measurement communication.

It might be worth breaking your project up into multiple chips. For e.g., reading 1-wire digital thermometer ICs is a blocking event, so your code stops running while that's happening. It's long enough to make the UI feel sluggish and unresponsive.

You could use an ATtiny or something to poll your sensor and send the temp back to your main controller. The rest of what you want to do is all fairly cooperative. For instance, serial I/O takes some time, but not that much. You're not going to miss the (less than a) millisecond it takes to transfer some data over serial before a touch is registered. Your PID routine probably won't miss the stolen ticks either.

Some code is forgiving of being interrupted, some isn't. If you do schedule events on interrupts, make sure your ISR doesn't affect critical timing loops or change the contents of any variables or ports that might need to be consistent during some other unrelated operation that could be happening concurrently.

What you want to do is simple if you go about it in the correct way. I would implement a finite state machine. It is a lot simpler than it sounds, google for more info.

Hey,

thanks for all the replies.

It sounds like I don't need to worry too much about time wasted for sending/receiving serial messages.

I should regularly check the state of the temp monitor and USB slave device, and store the values in variables so that they can be sent when requested (this will usually be once per loop anyway). eg

int temp;
unsigned long lastGotTemp; // time I last saved temp
int usbSlaveState;
boolean msgRcvd = false;

void getTemp(){
    char buf[bufLength];
    int temp;
    sendRequest("TEMP");
    lastGotTemp = millis();
    receiveRepy(buf);
    disablePIDInterupt();
    temp = atoi(buf);
    enablePIDInterrupt();
}

ISR pidControl(){
    // called when a timer over flows
    // using current temp etc values, calculate new output and applies it
    // restart timer
}

loop(){

    getTemp();
    getUSBSlaveState(); // similar to getTemp()

    if (msgRcvd ){
        // figure out what the display wants done
        // ...
    
        if(setSomethingOnUSBSlave)
            // set thing on USB slave
            
        if(someThingElse)
            // do other thing

       sendReplyToDisplay();
       msgRcvd = false;
    }

    automate(); // state machine function for opening closing valves etc

}

SerialEvent(){
    //Saves received characters to a circular buffer.
    // checks if full message received yet
    // if full msg received, sets a flag to true
}

If I used a finite state machine to process requests from the display, wouldn't I need to create a state for every single combination of requests i could receive from the display? as a received message could contain a request for several things at once eg current temp, set some value, get some other value ...

wildbill:
Just steer clear of delay.

Why should I steer clear of delay(), what should i use instead?

Should I have my PID function in an interrupt? Or should I do this in the loop also? I initially thought to use interrupts as it is pretty critical that it happens regularly (I'm meant to be aiming for +/- 5 milli Kelvin stability), which is why I also thought I would need to check the temp just as often.

Thanks again for your help

Should I have my PID function in an interrupt? Or should I do this in the loop also? I initially thought to use interrupts as it is pretty critical that it happens regularly (I'm meant to be aiming for +/- 5 milli Kelvin stability), which is why I also thought I would need to check the temp just as often.

With this statement, are you serious about asking this?

Why should I steer clear of delay(), what should i use instead?

If I used a finite state machine to process requests from the display, wouldn't I need to create a state for every single combination of requests i could receive from the display?

No a state machine tells you only what you have to do now, when you have done it you move the machine on to the next task.
By using this with the millis timer you can sort out what to do when. There is no need for a delay.

I agree with both veterans. I had no idea you were aiming for a few milliKelvins.
In that case create two variables called Setpoint and LastMeasuredTemperature.

Use a timer interrupt and put your PID code in the interrupt routine.
At first the PID code can be simply:
LastMeasuredTemperature = ReadTemperature();
OvenHeaterState = (LastMeasuredTemperature < Setpoint) ? true : false;
OutDigitalHeater (OvenHeaterState);

Your main loop() just deals with the user. It gets a possible user message to change the setpoint and changes Setpoint. In any case it then displays both variables to the touch screen display.

See how stable you get with that and then go PID math crazy if you need to!
See how fast your interrupt loop timer needs to be.

At first, for debug_mode only, you can have your pid loop print a running log of the temp messages it receives and the digital output actions it takes. For debug mode of your main loop, you can print the message received from the touch screen and a message that you are changing the Setpoint.

I'm meant to be aiming for +/- 5 milli Kelvin stability

Just out of curiosity, what are you using to measure the temperature to this accuracy?

I initially thought to use interrupts as it is pretty critical that it happens regularly (I'm meant to be aiming for +/- 5 milli Kelvin stability), which is why I also thought I would need to check the temp just as often.

The target accuracy and PID timing and not necessarily intertwined. For example: If you're controlling the temperature of a 2 ton slab of iron in a well insulated room using the heating element from an electric kettle, sub-second updates are pointless. Any output change is going to take days or weeks to have a measurable affect. However, if you're controlling the temperature of the tip of a pushpin in a windtunnel using a 10KW heating element than sub-second updates are critical.

In other words, before adding the complexity of an interrupt service routine you need to first determine that PID timing really is that critical.

"In other words, before adding the complexity of an interrupt service routine you need to first determine that PID timing really is that critical."

Agreed and well said. In this case, though, with an Arduino, it is so easy to set up a timer and interrupt loop, you can look at that as just a simpler and better way to organize the code. It's probably harder to architect the code without such a loop. Yes, it enables a faster control loop if need be, but even if it is not needed, it's an easy and proper place to put a separate loop to repeatedly read and control something. I'd describe it as making a software thermostat. You can modify an automatic adjusting algorithm on top of it, as you need it.

What temp sensor? Again, if it's 1-wire, you'll be waiting for hundreds of ms for your results every probe. Doing this, and waiting for the reply, more than once a minute or so means your UI will feel like it has locked up most of the time.

Hi thanks for the replies,

it seems to be split as to whether I should use an interrupt for my PID function. I like that having it in a seperate interrupt function seperates it out of the main loop (a finite state machine) and ensures that it will always happen. It wouldn't add too much more complexity, other than ensuring buffers etc aren't read/write at the same time, would it?

My environment can be extremely cold, down to 4K , and my heating element is only 5W. Would you agree the timing is critical in the situation?

SirNickity:
What temp sensor?

I'm using The model 14C from here http://www.cryocon.com/TMSelectGuide.asp . I will be using RS232, and it supports only half duplex mode.

PaulS:
are you serious about asking this?

Why should I steer clear of delay(), what should i use instead?

I re read the delay() reference. Stupid question, agreed.

Thanks again

My environment can be extremely cold, down to 4K , and my heating element is only 5W. Would you agree the timing is critical in the situation?

No I would not. There is nothing remotely time critical about thermal management, it can always wait the odd half second or so.

alphabetSoup:
My environment can be extremely cold, down to 4K , and my heating element is only 5W. Would you agree the timing is critical in the situation?

Nope. You haven't provided enough information. But it doesn't matter. You're convinced the PID timing is critical and it's your project.

Argh! Now I'm being ninja'd by Grumpy_Mike!

Though I said I would put it in an interupt handler routine for logical separateness, I have to agree with Grumpy_Mike.
This is most probably not time critical at all. You're even using slow rs-232 to communicate with it.

If it's in any way easier for you put the PID logic and commands in the main loop.
Even if you give the user a button to transmit back a history of measurements, and even if he hits that several times in a row,
you're certainly not going to have a wrong reading due to the measurement being delayed by even a few seconds.
Similarly your main loop will repeat many times per second. We are just guessing but one would think that
the cycle rate of thermostatic controller for an "oven" type device would be many multiples of seconds.

If you have any data on typical cycle periods for your milliK sensitive device, that would settle it.
If it turns out to be a hundred times or more greater than your measurement period (and we think it will), PID in interrupt will not be helpful.
Alternatively, if the oven has a small thermal mass and takes only a second or two of heating or cooling to change by 5 milliK,
then the PID should be in an interrupt. Consensus is that this will not be the case.

Consensus is that I don't need the interrupt as temperature control isn't really time critical, and the RS232 for reading the temperature is quite slow anyway.

I am unsure at this stage of how fast the volume I am controlling will heat up/cool down. I will need to run some tests first. If it turns out my code does not loop fast enough for it to effectively control the temp, I will look at using the interrupt.

Thanks for all the help, you guys were great.