Code structure for a large project

Hey all,

I'm making a system for my girlfriend to help control the temperature of a heated water bath, in which she dyes yarn.

For control, I'm using an Arduino Leonardo. I have a 16x2 LCD screen and a rotary encoder with pushbutton for user input. To monitor the temperature of the bath I have a DS18B20 temperature sensor. Finally I'm switching the heating element of the bath on and off with a solid state relay.

Now, I'm making good progress on the meat of the project, but I'd like advice on code structure. This is a larger project than most I've done on Arduino, and I'm finding it's getting unwieldy. Specifically:

Is there a better way to structure my finite state machine? I've abstracted the states into functions because it's clearer to read but it's still a bit messy. I'd particularly like suggestions on how best to manage on enter and exit conditions.

I take up a lot of code redrawing the screen when I may not need to do so. I've thought about having a function for each screen and only calling it on the entry condition, then just redrawing the section i need for that menu (for instance, the cursor position in the menu). Is there a better way to format it?

I've attached the current version of my code.

Thanks for any advice!

yarn_dyer_0.5.ino (17.2 KB)

With 12 states, you should use an enum to name the states, instead of using decimal numbers.

aarg:
With 12 states, you should use an enum to name the states, instead of using decimal numbers.

And, perhaps in doing so, you'll discover that there really aren't 12 states. Most likely, you have 3 or 4 states, some of which have sub-states.

Each "state" is executing too much code. There are actually 3 parts to each state of an FSM: code to execute when you enter that state (once), code to execute while you're in that state (continuously), and code to execute when you exit that state (once). Currently, you have all 3 parts in each state routine, and the ENTER and EXIT code gets executed continuously.

For example, the LCD printing should actually be executed only when you enter that state. Checking the encoder and button should be performed continuously. The Serial.print might be considered the code to execute when you exit that state (via state variable assignment).

Because the transitions between each state are an arrow (on a diagram), it can be arbitrary as to whether you consider it exiting the previous state, entering a new state, or both. Lots of FSM code gets written with only an exit or an enter section.

It may get down to how much you want to isolate these sections from each other. For example, if the LCD prints for the "ChangeTemp" were performed in the "Temp" state exit code, then you might say that "Temp" knows too much about "ChangeTemp". In this application, it seems like you have mostly ENTER code that prints the LCD for a particular STATE. The continuous code simply checks the encoder and button an transitions to the next state (no EXIT code, except for a debug statement).

You can either split each state routine into 2 or 3 routines, or have the same routines with "guarded" sections inside (i.e., guarded ENTER code, unguarded state code, guarded EXIT code).

Cheers,
/dev