Topic: Help with Design: Using processor time efficiently when dealing with serial (Read 2191 times)previous topic - next topic

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

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??

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.

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 ...

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.

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.

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.

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 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.

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?