Stator Library Makes Your Arduino Code Easier To Read

The readability of your code can make the difference between your project being a joy to work on, or an absolute headache. This goes double when collaborating with others. Having easily parsed code reduces your cognitive load and makes solving problems easier. To try and help with this, [PTS93] developed the Stator library to make certain common tasks simpler to read.

The aim of the library is to get rid of piles of state tracking variables and endless if/else statements – hence the name. It’s designed primarily for the Arduino IDE but doesn’t have any dependencies on the API, so can be used in other C++ environments. It comes with a variety of neat tools for common jobs, such as reading an analog sensor with hysteresis around a trigger point, as well as easy ways to track state changes across multiple variables. By using basic English terms instead of condition checks and mathematical operators, it can make things more readable and easier to follow.

The power of the Arduino platform has always been in its easy to use libraries that make everything easier, from interfacing LCDs to working with Amazon Dash buttons.

56 thoughts on “Stator Library Makes Your Arduino Code Easier To Read

  1. States are great, but really, just naming your variables and constants something interesting to begin with is an awesome first step for a lot of people! Get rid of those “magic” numbers which seemingly appear out of nowhere.

      1. Arduino isn’t a desktop computer… Many of the people never really learned to program, before the started on Arduino. Just like many of them never really learned electronics, since they use the ready-made modules, and just need to connect wires to the header pins.

        My experience, I just recently started using Arduino, mostly because there are a lot of modules in the surplus stores really cheap. Strip board, perf board, even etching your own boards, is sort of a side project of their own. I never learned C, mostly because I’m not a structured programmer, so stuck with BASIC and machine language, from my 8-bit computer programming days. I play with ATTiny microcontrollers some, but it was a lot of work, then my programmer died. So, Arduino started to look pretty good, everything already on a board. plugs right into the USB port. I still don’t like C, but learning how to get around. I’m more of a code-pirate though, I’ll pick through someone else’s code, for the parts I need in my projects, still have to make some changes, but works. I don’t find it too terrible, unless they use higher level functions, or heavy into library functions.

        Arduino is really all about programming, it’s a necessary evil, which some people just push through, best they can. I don’t see myself using libraries like this, just more work and confusion.

      2. I disagree, the library would be helpful just as Lewin makes clear, it lowers the cognitive load. If you have two values you have to track two values and you have to check and update them consistently everywhere. It’s a bug waiting to happen. The Stator class would be more trustworthy because it takes care of both values.
        It could be very good if the Stator class were shown with an enum of defined states for a state machine as well:

        enum MachineState {Initialise, WaitForCommand, Work, ReadTemperature};

        Stator machineState;
        machineState = Initialise;

        Calling an object of the Stator class ‘mystuff’ is a bad idea though, that is clear.

        1. Agree.
          I get a laugh every time I see “my” anywhere in code. Waste of characters! No matter who is reading it, it’s “mine”! So therefore it specifies nothing at all – except in very special cases where the words server and client or master and slave would have made more sense anyway.

  2. If you really think that “mystuff.changed()” is more readable than “state != lastState”, I don’t know what to say. Adding a function/method called changed() just means that you have to go look at that function to know what it’s checking.

    It’s this kind of adding code for no good reason that leads to both code bloat and insanity. Anybody who’s the least bit familiar with C/C++ knows what the “state != lastState … lastState = state” construct does, because it’s all right in front of you. NOBODY knows what the changed() function means without looking it up.

    1. By the way, the examples on the github page, with and without the library… they’re not even functionally equivalent. One resets the “old” variables only if both conditions are met (both values other than their respective old variables), but the library changed() method checks whether the respective values changed with their last assignment. Different behavior. And that’s the problem with these “helpers”. You forget what exactly they’re doing.

    2. +1

      But I would like to add that ALL code is difficult to understand (some more then others).
      So usefull comments should be added. See is as a note to yourself in the far future (3 months from now).
      There’s no shame in writing comments in your code. Some people are lazy others are arrogant and think you won’t need it, but many think that they will add it later… but somehow they never do. And so you’ll end up with code that is difficult to read when you see it the fist time. Especially when variable names are badly chosen, numbers instead of enums are used and brackets are left out or are all over the place. A proper case statement can improve a simple if ifelse ifelse else structure in a few seconds… etc.

      BAD HABBIT:
      writing code without any comments

      BAD COMMENT: (writing exactly what each line does and not what it means as a whole*/
      /*check if flag is set, loop until cleared*/
      /*copy data from buffer to SSP register*/
      /*increment pointer*/
      /*decrement variable*/
      /*check if variable is zero*/
      /*do again*/

      GOOD COMMENT:
      /*transfer all stringdata to serial port*/

      1. Yes, the frustration you’re saving is likely your own! I find the best comments are along the lines of “what was I thinking” and “why did I structure this the way I did”. No sin to put a short paragraph in here and there.

          1. I prefer comments as specifications. The comments explain what the code *should* do or what the objective is, rather than what the code does or how it does it. The comments can then be written before the code PDL-style and form the basis of unit tests too. Such comments only disagree with the code when the specification changes rather than merely the implementation.

          2. +1. Every function should begin with a specification. What the parameters mean, what it does, and what it returns. Also, every source file should indicate what group of functions, classes, or whatever, are in it. I’ve seen WAY too many “open source” projects whose files begin with their legal statements, then jump right into code.

    3. Not trying to be snarky, but.. So there is a library for hiding the “complexity” of integer comparison? What next, will there be a isSmaller(), isBigger() and isEqual() too, as the “” and “==” are equally complex concepts? How about localized versions for us with English as second or third language?

      Perhaps one should try using the visual programming languages, if the normal mathematical operations are too hard to read.

    4. Well, mystuff.changed() is quite readable in my opinion. But that’s not the biggest worry I have. The Stator class is also keeping track of time, which adds more functionality to the class than you’d need. There’s no need for this in the first place!
      The other drawback in my opinion is that this library requires a C++ compiler while it’s more practical to limit yourself to C without the classes, as that’s easier to use. Classes can make things slightly more complex but also tend to eat up more memory.
      Besides, if you want to implement something like this then it’s easier to implement an OnChanged() event instead by using a callback function that gets called whenever the value changes.
      But it’s not good as it requires a bit more code for implementing a simple check.

      1. The idea is that as soon as you have more than variable to track you get a lot of repeated code and a the simple state != lastState becomes state1 != lastState1 && state2 != lastState2 && state3 != lastState3 and all of its accompanying logic code to handle the state transition.

        So you gotta make sure you have written the logic correctly for each of the states. Small mistakes creep in fairly quickly so its easier to rely on a smaller construct that behaves the same every time and isn’t prone to you forgetting an assignment for state3.

        I mostly do rapid prototyping. More abstraction to keep me from repetitive errors is helpful.

        If it doesn”t help you that’s perfectly fine. It’s not a “this fixes everyone’s problems” library.

        1. Doing fast prototyping is no excuse for writing bad code. When you start to use state-flags that are either true or false, you’re doing it wrong! This is when you would need bitfields instead, where you can use a single int to keep the state for 16 to 32 different boolean flags in a single variable. So yeah, you would need to define a few constants like STATE1TRUE=1, STATE2TRUE=2, STATE3TRUE=4 and even STATE1AND3TRUE=STATE1TRUE || STATE3TRUE. Then you can compare if (flag && STATE1AND3TRUE==STATE1AND3TRUE) to check for multiple flags all at once…
          You can also check if (flag && STATE1AND3TRUE==STATE1TRUE) to make sure state 1 is true and state 3 is not. Or check for STATEFALSE (=0) to check if both are false…
          Using bitfields is just something you should get used to. The related AND/OR/NOT/XOR logic tends to be challenging for new developers but once you understand the concept, it should be relatively easy. And for more complex situations, you can always write things down to work it out. With pen and paper!

          1. Wasn’t meant to be a comment to you but to BrightBlueJim.

            Anyways, you honestly think that construct you describe is easier to read?

    5. That’s exactly what I was thinking with the given examples. It’s so safe to use “!=” to represent “not equal to” that I don’t remember the last time someone that wasn’t a programmer asked me what that meant or showed any sign they didn’t understand what it meant.

      Last time I tried to work with a programmer that insisted on writing stupid “helper” functions like “changed()”. I began to pepper the code with alternatives like: “state ^ lastState”, “state – lastState”, “~(state | lastState) == ~state & ~lastState” and a boatload of other methods of testing equivalence reaching all the way into linear algebra. Really didn’t take long for that dude to stop wasting time on such functions.

  3. My pet peeve is ‘{‘ and ‘}’ !!
    Sitting through a code review and the author of a piece of code can not find the start and end of his functions, because he could not find the ‘{}’ in the B/W editor that everyone loves to hate.
    White space is just as important as useful variable names.

    Isn’t that why python uses tabs/indents to make the code more readable ??
    Even the interpreter enforces that.

    Why can’t the C/C++ people take the hint ?

    1. Tabs and spaces are not more readable.
      Ive spent hours looking at other peoples code trying to find out why somethings not working only to find they put a tab in the wrong place. Looking for something thats not there is a lot harder than trying to find a “}” put in the wrong place.

      Theres no excuse for being lazy and not properly setting your code out in a readable form.
      Enforced white space in a chunk of code is not a remedy. Its just easier to make it unreadable.

      1. Even the very simplest editors have a bracket matching function. And most of the C or C++ I read – and all that I write, does also use indentation to make things clear. Further, I rarely put a bracket on the same line as code.
        There they are, with whitespace after.
        You can do this sort of thing with any language I know of.
        Languages that force me to do it their way and insult my intelligence don’t get used around here.

    2. What are you talking about?

      He isn’t missing any brackets in his code.
      In one part of your comment you seem to be whining that you don’t like brackets and prefer Python style where grouping happens by whitespace. In another part you seem to be complaining about someone not using brackets, saying he could not find the keys.
      Well which is it?

      Also for every editor there are a lot of people that love to hate them. What editor is limited to B/W? Even the simplest commandline editor runs in a terminal where the user can usually choose whatever colors. You seem to think it is obvious which editor you are talking about but I am not sure you even know.

      As for white space.. you are right. That is important too. But most programmers I deal with seem to already know that. The lesson I think people need to learn is that TOO MUCH whitespace is just as bad as too little. Too many spaces per indent means the eye has to scan left/right more. Scanning left and right is a great way to lose one’s place and makes concentrating on the meaning harder. 3 spaces is plenty, 2 is good with a blocky font! Also, devoting a line to an opening bracket? WHY! It does nothing for readability, it just means more scrolling. Arguably that is better than left/right scanning but extra scrolling for no benefit is still a detriment. Put your opening brackets at the end of the method name, conditional or loop statement. Do not put it on the next line down.

      Oh, and the way indentation is handled for switch statements in pretty much every style guide and as the default behavior of pretty much every editor… OMFG, WTF is wrong with people?!?!?!

      Ok, that’s enough. /rant

  4. I can see what it might be useful for the author, but does that level of abstracting help? And is really clear what they are doing, or are we just adding a layer of bloat?

    Clean code would probably be a better start – he could start with putting the ‘{‘ in the right place…

  5. It would be much better when Arduino coders learn to code more properly. Seriously, if these coders did a better job then this library would not be required!
    The C language has an excellent tool for this, which happens to be the bitwise operations. This allows you to hold various states inside a single variable. The use of bit-fields is generally preferred as it saves memory. Interestingly enough, you can actually use multi-bit fields in your structures like this:
    struct {
    unsigned int suit : 2;
    unsigned int value : 4;
    unsigned int face : 1;
    unsigned int state : 1;
    } card;

    And yes, that would create an 8-bit playing card for the four suits (clubs, spades, hearts and diamonds) plus the card value from 0 to 15 there 0 would be a joker, 1 the ace and 11, 12 and 13 the Jack, Queen and King, allowing you to add two more card values. The face would be an indication if the face value is up, thus visible and the state would indicate if the card is still in the game or not. Or whatever you like, really. So you can check card.face to see if it’s face up or down or card.value to check it’s value. And if need be, it can be streamed quite easily as it’s just a single byte in total…
    You can make larger fields too, btw. For example, a whole deck might be defined as:
    struct {
    unsigned int spades: 13;
    unsigned int clubs: 13;
    unsigned int hearts: 13;
    unsigned int diamonds: 13;
    } deck;
    This is 52 bits in total but the compiler will turn it into 64-bits. And every field in the structure would indicate if that specific card is inside the deck or not. You can also use this structure to represent the hand of a player or even check for specific winning patterns. You can even use the 12 remaining bits for additional states about the deck, if need be.
    But bitwise operations like these are too challenging for most, even though they make coding a lot easier when properly used…

    1. I’m not really sure what you’re trying to say with this, the helpful thing about the library isn’t the fact it puts two values into a single object, it’s that it uses C++ operator overloading to ease the burden of tracking the state. If I’d been implementing it I wouldn’t have gone for the two templated versions, but that’s neither here nor there.

      1. I’m just saying that making a generic library to handle states is a bit overkill and leads to unnecessary code. You can have multiple flags inside a single variable by using bitwise operators AND/OR/XOR/NOT logic together with a list of constants or predefined values. And in case you need more than just boolean flags you can also just use bit-fields within a single type and specify the length in bits for every value. My example is a card game where I can store a whole deck inside a single int or a single card with two states in a single 8-bit value.
        All this without using C++…
        And as I said, the library is doing way too much, yet doesn’t do the important things… It keeps track of the age of the variable, which is a bit pointless. But it also doesn’t do anything useful when the value is changed. It would be useful if it calls a callback function when the value is changed so you respond immediately on any change.

  6. The art of using scruffy code to make others’ code readable ;-)

    bool changed(){
    if(_state != _lastState){
    return true;
    }else{
    return false;
    }
    }

    should be written

    bool changed() {
    return (_state != _lastState);
    }

    (and we are speaking of microcontrollers, where cpu cycles and memory are a concern)

      1. I would put three spces before that return keyword, but this style is better. Sometimes, I have had to print the code and draw lines with a marker to find some misbehaved “}” …

  7. I am not a proper programmer, but I seem to code a lot of state machines. And I use “switch”

    static int state
    switch (state){
    case 0; // Idle
    if read_input() <= 0 break;
    state = 1;
    timer = 10000;
    // fall-through
    case 1; // wait for reset
    if (timer -= fperiod) 0) break ; // continue waiting
    state = 3 ;
    // fall-through
    case 2;
    case 3;


    and so on

    }

  8. People… Just switch over to PlatformIO on Visual Studio Code (or whatever IDE) and enjoy updates, code formatting, linting and a stable IDE…
    Just make sure to do it on a rainy day, it will take you some time to get used to the new flow.

    1. Sublime text for me, but I even use gedit…any modern editor is decent these days. All match brackets easily, almost all are better than the Arduino IDE…

      All have updates, and the list of languages that syntax highlight might be a better criterion. Or the ability for refactoring to really work despite odd rules in some language – where the same name can be used as a function or a variable depending on context…

  9. Many opinions, and many of them well reasoned. I started to read the article and thought I disliked the idea, then considered I have also created simple functions in the past just to simplify reading the code, when tired or in a hurry.

    Lewin´s solution is not for all people, nor is something that should be enforced. It Is just one more suggestion of ways to make your code more readable to yourself, mainly, and a way to induce thioughts in “how could I make my code clearer ” .

    In the case of his examples, if one uses the state comparision in just one place, no need to have another abstraction and the bloat that comes with it. But like my first coding teacher taught us, “if you have to repeat that piece of code in a lot of places, it should be turned into a function”.

    1. It doesn’t surprise me, it is new if you haven’t been taught it.
      The problem is the crappy way we’ve documented and communicated our “innovations” and “passed” that knowledge on to others.

Leave a Reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.