Code outline, flagging functions so they don't loop

Hi All, apologies in advance for a longish post. I made some smart shades with an esp32 and rotary encoder motors but i made it in the laziest most taxing possible way which resulted in it working for a few weeks and eventually freezing. Now with some time in quarantine I want to revisit it the right way. My original iteration was just brute force, especially on the nvs, writing and recalling many stored data points every time through loop even if they were just sitting there.

Before I put together the actual syntax correct code, I made a rough outline of the functions and I was hoping you kind folks could help me figure out if I'm thinking about this the right way, as I have very limited experience and have never made a project that sets flags to only do certain things certain times through the loop.

Most of this stuff already works so what I'm really trying to get right is the big picture flow of the project and whether my functions make sense. I'm trying to get it to be as efficient as possible with only recording the location to nvs once it gets to a stopped position and then basically stop doing everything until it gets a new command to move. That's the only trigger for it to do anything so it doesn't need to even monitor the encoder position in between getting to a target location and getting a new command. I've pasted my outline below and would appreciate any input before I start coding this out with actual syntax

Global Variables
   Int Ratio
   Int SwitchLevel
   Int Leeway = 40 //gives a buffer to motion to stop bouncing trying to land exactly
   Int Sleep = 0
   Int Flag = 0
   
Callbacks
1) If message arrives with "switch level X" {
       Set Sleep=0
       Set flag = 0
      (Add unsleep stuff, wakeup encoder, recall encoder position)
      Set SwitchLevel = X
   
2) If message arrives with "Ratio X"{
       Set Ratio=X}


Loop(){

     If sleep =1 {
          Do nothing at all;}

     If sleep =0 {
               If (need to go up) [SwitchLevel*Ratio > encoder position-leeway] (the actual triggers work in my project so don't worry about that) {
               Do GoUp()}
     
               If (need to go down) [SwitchLevel*Ratio < encoder position+leeway] (these triggers work so don't worry about that) {
               Do GoDown()}

               If (within target range) SwitchLevel*Ratio=encoder ± leeway (have this coded out already but maybe if, if, else if for these three?){
               Do Arrived ()}
}

-----Functions -----

GoDown()
     Start motors downwards
     [Set flag = 0 don't think I need because getting the command to move sets the flag anyway]

GoUp()
     Start motors upwards
     [Set flag = 0 don't think I need because getting the command to move sets the flag anyway]
     
     
Arrived ()
     Stop motors;
     If flag=0{
           Do Memory ()
     If flag=1
        Set Sleep = 1 
        (Sleep things: turn off encoder, etc)

Memory()
         Record everything to nvs
         Set flag = 1

Please let me know of anything thoughts, I'm happy to clarify any points if helpful because this is just a skeleton of part of the project so it might be hard to follow

What does 'flag' do?

aarg:
What does 'flag' do?

Hopefully, keeps track of when the location and encoder data was last written to nvs to stop it from writing the information every time through the loop (loop, within range so looping function "arrived" which would keep writing to nvs. This (hopefully) writes everything to nvs the first time through the loop after arriving, sets flag to 1, loops back through arrived but because flag is 1 it doesn't write to nvs and instead sets sleep to 1 which then doesnt do anything until a new command comes which resets sleep and flag to 0) am I thinking of this correctly?

Anyone?

In my own programming I find it very useful to use meaningful names for variables so that the code almost reads like English. It makes illogicalities stand out.

I think a better name for your "flag" would be "motorMoveFinished" (a boolean variable).

And it may actually be useful to have some other variables to keep track of things. For example you might have pseudo code like this

if (buttonPressed) {
   if (motorMoveFinished == true) {
       if (blindAtBottom == true) {
            motorDirection = 'U';
       }
       else {
           motorDirection = 'D';
       }
       motorMoveFinished = false;
    }
}

if (motorMoveFinished == false) {
    if (motorDirection == 'U') {
        // make motor move up
        if (upperLimit == triggered) {
            blindAtTop =  true;
            motorMoveFinished = true;
         }
     }
     else {
       // make motor move down etc
     }
}

...R

What does nvs have to do with it?

a7

alto777:
What does nvs have to do with it?

I have been assuming the OP plans to write the position to EEPROM memory (non-volatile-storage)

...R

alto777:
What does nvs have to do with it?

a7

The poster below is correct. There's no homing switch, so on first boot the encoder starts at zero and the blinds are fully open. From there when whenever it moves it it writes the encoder position and the last target location it received so in the event of a power loss, when it starts up it's still homed. The first iteration was brutally taxing and lazy, I had everything in loop so it was constantly storing and recalling from nvs around the clock. It worked perfectly but crashed after a month. This thread/reiteration is driven by replacing that system with a more efficient option that only writes and recalls after moves. The next iteration will probably have a millis timer in the go up and go down sections to store the location once a second while in motion to. That's probably less of a practical benefit and more of just feeling like that's the right way to do this in case the power fails mid move. I only adjust the blinds maybe two or three times a day and they're pretty fast, so it's so extremely unlikely the power will go out during the 15 seconds of a day that the motors are actually in transit!

The EEPROM has a 100,000 write cycle limit. If you write once a second, that will last 28 hours.

aarg:
The EEPROM has a 100,000 write cycle limit. If you write once a second, that will last 28 hours.

what happens after 28 hours then? Could I wipe the board clean and start over? What about reading from nvs?

While that sounds like bad news, if I write once a second only while moving, and I move the blinds on average of three times a day for roughly 5 seconds a move, the eeprom should last at least 15 years by my math. If I skip the once a second while moving and just do it once at the end up each move, the eeprom would easily outlive me.

The trick is, programming it so it actually only writes to nvs at the end of each move, which is the goal and purpose of the thread, to make sure my big picture approach/outline actually makes sense as an efficient way to only write at the end of moves and not constantly every time through loop like the lazy version had been doing!

Thanks again!

Robin2:
I have been assuming the OP plans to write the position to EEPROM memory (non-volatile-storage)

...R

Yes, nvs, thank you. Apparently you assumed correctly. Not how I would solve the problem, but everyone for themselves.

I have just been poking around in my defunct garage door opener and I was surprised to see a little "analog computer" inside, a linear slide mechanism of a lead screw driven by a gear tap on the main drive shaft of the big motor, so a truck with little switch blades on it mirrors what the door is doing.

The other switch points are on lead screws that are what you turn to "program" the door to go up and down only so far.

It surprised me to see this in the 21st century, but it is probably a design that's been around and quite adequate and reliable for decades. All it has to do is outlast the power section - my opener failed because the main gear wore out, no component level repair available.

So long winded, but I'd think about adding some limit switches and forget about nvs (non-volatile storage).

a7

aarg:
The EEPROM has a 100,000 write cycle limit. If you write once a second, that will last 28 hours.

I had the impression (perhaps wrongly) that the OP would only update the EEPROM at the end of every blind movement - which would probably only be a a few times per day. At 100 times per day one byte of the EEPROM should last about 3 years, and there are 2000(?) EEPROM bytes which would probably outlast a human life if wear-levelling is applied.

...R

Robin2:
I had the impression (perhaps wrongly) that the OP would only update the EEPROM at the end of every blind movement - which would probably only be a a few times per day. At 100 times per day one byte of the EEPROM should last about 3 years, and there are 2000(?) EEPROM bytes which would probably outlast a human life if wear-levelling is applied.

...R

Right, that's the plan, and the main point of this post which I would summarize as "is this code outline, once programmed, an effective/efficient way of only writing to nvs at the end of each travel move." I'm throwing around the idea of also doing periodic writes while in motion to account for an incredibly unlikely power failure while the blinds are moving, but will probably not bother. It would be easy to do, but so unlikely to be necessary. I've lost power once in the last decade, the blinds are in motion for 15 seconds a day, the odds are absurd. Plus worst case scenario, if the blinds reboot and don't have an accurate stored location, they'll just spin in circles at the top or fall off the roll at the bottom.

So the main question stands, does the flow I've laid out in the outline make sense for flagging when memory needs to be recorded or not, such that it'll only record at the end of a travel move but always record at the end of a travel move? Is there a better way to do so?

clearlynotstefan:
So the main question stands, does the flow I've laid out in the outline make sense

Have you carefully read all of the Replies you have received?

...R

Robin2:
Have you carefully read all of the Replies you have received?

...R

I have. I'm not seeing commentary on arriving/nvs part of the flow I've laid out, apologies if I'm missing it. I can rename the variable certainly (and I plan to), and add some new variables. The motion and trigger side of the program already works great (though I'm always open for improvement suggestions), it's the approach to setting that flag variable and then looping back through the Arrived makes sense/if there's a better approach.

The motion and trigger side discussed above is more or less what I'm already doing in loop: if encoder position < target position do GoDown() if [the opposite] do GoUp (), if porridge just right, do Arrived (). I certainly could set a variable for motor direction in each of them, but the library I'm using just has two pins to control direction so in goup and godown I'm just using a=1 b=0, and a=0, b=1, respectively, with arrived changing to 0 and 0.

If it makes more sense to structure it differently I'm all ears, but the blinds are going where I want them to go and stopping where they need to so I'm happy with that particular aspect. The section where I'm arriving is the big change for this iteration, doing the arrived function, saving nvs and setting a flag, looping back through and not saving again because the flag is set, then going down the other path of doing nothing until a new command comes to set the flag back to 0, is the part I'm especially looking for input on, if possible. And again if I did miss something, my apologies as I very much appreciate any input on this project!

clearlynotstefan:
I have. I'm not seeing commentary on arriving/nvs part of the flow I've laid out, apologies if I'm missing it.

In the piece of code I suggested in Reply #4 I would save the value at the point in the program where motorMoveFinished is changed to true

...R