Global Variable, Delays and Repeat problems

Hi,

somehow I am stuck since 5 hours :smiley: :

What I want to do:
I want to repeat the up and down with the press of a template button.
With press of template button 2 I want to stop but only if I am in position 2.
(of course I have to stop Template Button 1 again, but I don’t want to put too much problems into this topic now ^^)

What my problems are:

  1. The delay seems to overwrite open and close_duration as long as delay is lower than duration.
    (Problem solved by workaround if I add the durations on the delays.)

  2. Where do I find the ESP_LOGD log? In the logbook are no entries about global variable “position”.

  3. If I remove all the lambdas I really have intented repeation of 2. If I insert lambdas its endlessly repeating. Why?

  4. How do I read out the global variable “position” in template button 2? If I don’t comment it out I get error: Unable to find condition with the name id(position2)

esphome:
  name: little_wemos
  platform: ESP8266
  board: d1_mini
 
wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
 
  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Little Wemos Fallback Hotspot"
    password: "************"
 
captive_portal:
 
# Enable logging
logger:
 
api:
  services:
    - service: control_servo
      variables:
        level: float
      then:
        - servo.write:
            id: my_servo
            level: !lambda 'return level / 100.0;'
          
 
ota:
 
output:
  - platform: esp8266_pwm
    id: pwm_output
    pin: D3
    frequency: 50 Hz
 
 
servo:
  - id: my_servo
    output: pwm_output
    
button:
  - platform: template
    name: "Template Button"
    on_press:
      - repeat:
          count: 2
          then:
            - logger.log: Button Pressed
            - cover.open: flying
            - lambda: |-
                id(position2) = 1;
                ESP_LOGD("main", "Global value is: %d", id(position2));
            - delay: 8 sec
            - cover.close: flying
            - lambda: !lambda |-
                id(position2) = 2;
                ESP_LOGD("main", "Global value is: %d", id(position2));
            - delay: 9 sec

          
  - platform: template
    name: "Template Button 2"
    on_press:
      - logger.log: Button 2 Pressed
      - wait_until:
          id(position2) = 2  
      - cover.stop: flying

        
            
cover:
  - platform: time_based
    id: flying
    open_action:
#      then:
      - servo.write:
          id: my_servo
          level: 5.0%
    open_duration: 5sec
          
     

    close_action:
#      then:
      - servo.write:
          id: my_servo
          level: -10.0%
    close_duration: 6sec
    
    stop_action:
#      then:
      - servo.write:
          id: my_servo
          level: 0%
      - servo.detach: my_servo
      
globals:
  - id: position2
    type: int
    restore_value: no
    initial_value: '0' 
  

Perhaps it’s the easiest task for one of you professionals ^^

Regards
maxpd

Perhaps we can reduce it to questions 3 and 4?

At what line do you get the compiling error?

After a quick glance at the code, at least the following line caught my eye:

- wait_until:
    id(position2) = 2  

This is c++ code, however you don’t declare it as such. As a result the compiler tries to compile it as yaml, which obviously gives an error.

I think it should be:

- wait_until:
    lambda: |-
      return id(position2) = 2;

instead.

Thank you assembly,

I have to admit that I didn’t really understood the lambda logic until now and am really wondering why I have to return the id if I want to do the contrary: reading it out as it was saved (returned) before^^

But nevertheless I changed the code to your advise and now the error is really concrete and aims on your lines ^^

Compiling /data/little_wemos/.pioenvs/little_wemos/src/main.cpp.o
/config/esphome/esphome-web-dda7e1.yaml: In lambda function:
/config/esphome/esphome-web-dda7e1.yaml:70:33: error: cannot convert 'std::__cxx11::basic_string<char>' to 'bool' in return
   70 |             return id(position2) = 2;
      |              ~~~~~~~~~~~~~~~~~~~^~~
      |                                 |
      |                                 std::__cxx11::basic_string<char>
*** [/data/little_wemos/.pioenvs/little_wemos/src/main.cpp.o] Error 1
========================== [FAILED] Took 6.15 seconds ==========================

so I changed additionally std::string to int and it is working so far without compiling errors only hints. But now the Template Button 2 stops the servo immediatly and not in position 2.

So I assume position isn’t really written / read
(no entries in logbook)

If the next step in the repeat section starts, the servo is also turning again.

To avoid the new beginning I tried to add a “not condition”

on_press:
      - while:
          condition:
            not:
              lambda: |-
                return id(position2) = 2; 
            then:
            - repeat:
                count: 2
                  then:
                  - logger.log: Button Pressed
....

image

You are returning the outcome of

id(position2) = 2;

(which will be either true or false) to the “wait_until” function.

To be honest, I don’t really understand what you’re trying to do. I have the suspicion that you are making it more complicating than it has to be.

Could you explain once more what it is you’re trying to achieve? Are they some kind of blinds?

Ok understood the intention of returning. Many thanks!

Of course: I want to pull and push something along a long rope.

To achieve that I want to push it a specified time and speed and pull it again a specified time and speed with a pause inbetween.

As I am getting problems if I stop it somewhere in the middle of the run (and start the next run again with the specified time but from the half of the rope) I wanted to wait until it reached the start position again before I end the run.

How do you determine the position?

I would determine it this way: If it reached one end and pauses there it gets a position number for one second.

then:
            - logger.log: Button Pressed
            - cover.open: flying
            - delay: 8 sec
            - lambda: |-
                id(position2) = 1;
                ESP_LOGD("main", "Global value is: %d", id(position2));
            - delay: 1 sec
            - lambda: |-
                id(position2) = 0;
                ESP_LOGD("main", "Global value is: %d", id(position2));
            - cover.close: flying
            - delay: 9 sec
            - lambda: !lambda |-
                id(position2) = 2;
                ESP_LOGD("main", "Global value is: %d", id(position2));
            - delay: 1 sec
            - lambda: !lambda |-
                id(position2) = 0;
                ESP_LOGD("main", "Global value is: %d", id(position2));

This would be my complete script where I would expect my intented functionality.

No compiling errors, only suggestions.

But negative side: Template buttons don’t have any functionality anymore.

Compiling /data/little_wemos/.pioenvs/little_wemos/src/main.cpp.o
/config/esphome/esphome-web-dda7e1.yaml: In lambda function:
/config/esphome/esphome-web-dda7e1.yaml:55:33: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
   55 |                 return id(position2) = 3;
      |                 ~~~~~~~~~~~~~~~~^~~
/config/esphome/esphome-web-dda7e1.yaml: In lambda function:
/config/esphome/esphome-web-dda7e1.yaml:87:33: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
   87 |             return id(position2) = 2;
      |              ~~~~~~~~~~~~~~~~~~~^~~
esphome:
  name: little_wemos
  platform: ESP8266
  board: d1_mini
 
wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
 
  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Little Wemos Fallback Hotspot"
    password: "************"
 
captive_portal:
 
# Enable logging
logger:
 
api:
  services:
    - service: control_servo
      variables:
        level: float
      then:
        - servo.write:
            id: my_servo
            level: !lambda 'return level / 100.0;'
          
 
ota:
 
output:
  - platform: esp8266_pwm
    id: pwm_output
    pin: D3
    frequency: 50 Hz
 
 
servo:
  - id: my_servo
    output: pwm_output
    
button:
  - platform: template
    name: "Template Button"
    on_press:
      - lambda: |-
                    id(position2) = 0;
                    ESP_LOGD("main", "Global value is: %d", id(position2));
      - while:
          condition:
            not:
              lambda: |-
                return id(position2) = 3; 
          then:
            - repeat:
                count: 2
                then:
                - logger.log: Button Pressed
                - cover.open: flying
                - delay: 8 sec
                - lambda: |-
                    id(position2) = 1;
                    ESP_LOGD("main", "Global value is: %d", id(position2));
                - delay: 1 sec
                - lambda: |-
                    id(position2) = 0;
                    ESP_LOGD("main", "Global value is: %d", id(position2));
                - cover.close: flying
                - delay: 9 sec
                - lambda: !lambda |-
                    id(position2) = 2;
                    ESP_LOGD("main", "Global value is: %d", id(position2));
                - delay: 1 sec
                - lambda: !lambda |-
                    id(position2) = 0;
                    ESP_LOGD("main", "Global value is: %d", id(position2));

          
  - platform: template
    name: "Template Button 2"
    on_press:
      - logger.log: Button 2 Pressed
      - wait_until:
          lambda: |-
            return id(position2) = 2;  
      - cover.stop: flying
      - lambda: !lambda |-
          id(position2) = 3;
          ESP_LOGD("main", "Global value is: %d", id(position2));

        
            
cover:
  - platform: time_based
    id: flying
    open_action:
#      then:
      - servo.write:
          id: my_servo
          level: 5.0%
    open_duration: 5sec
          
     

    close_action:
#      then:
      - servo.write:
          id: my_servo
          level: -10.0%
    close_duration: 6sec
    
    stop_action:
#      then:
      - servo.write:
          id: my_servo
          level: 0%
      - servo.detach: my_servo
      
globals:
  - id: position2
    type: int
    restore_value: no
    initial_value: '0'
  

Your template button currently does the following:

8 seconds forwards to arrive at position 1
1 second forwards to arrive at position 0
9 seconds backwards to arrive at position 2
indefinitely backwards to remain at position 0

The

- delay: 8 sec

in your code doesn’t actually stop the motor from moving forward. Additionaly you should explicitly tell the motor to stop by using

- cover.stop: flying

I still only partially understand what you’re trying to achieve. Perhaps telling us what application you have in mind for this code makes it easier to understand (blinds?, an airvent cover?).

I want to push a figure along a rope and pull it back, push it again, pull it back again. Several hours long.
As the servo doesn’t know a physical position of the figure I have to stop it if it reaches a specific position, independent from the time when I push the stop button.

if I remove

while:
          condition:
            not:
              lambda: |-
                return id(position2) = 3; 

in fact my template button really does at the moment:

5 seconds forwards (open_duration)
3 seconds pause (8s delay - open duration) (motor is really stopping)
writing position 1
1 second further pause
6 seconds backwards (close_duration)
3 seconds pause (9s delay - close duration)
writing position 2
1 second further pause
starting all over from new again a second and last time

Okay. You’re right.
I was looking at your code snippet in post 8. Your total code in post 9 does stop the motor.

One thing to keep in mind though, is that timed positioning is quite inaccurate. The distortion will increase the longer the device is running.

Is it possible to implement some kind of triggered positioning (some kind of end-switch, reed-switch, or IR-switch) at the beginning and at the end of the track?

Yes I also thought about end stops. But will be difficult as the rope is hanging in the air and has a lenght of 30 meters. Difficult with electricity and wlan range.

Perhaps the rope is long enough to have a puffer for time delays. I also wanted a complete independent setup from wlan interruptions and so on.

But i also faced the problem yet that the first open only last 2 instead of 5 seconds…

That sounds like a very interesting contraption you’re building. How are you pulling a 30 meter rope with a servo?

Perhaps in order to simplify the config a bit, you could use the cover state. That would eliminate the need to track the positions with numbers and a global variable.

As for the finishing of the loop after pressing the stop-button: perhaps you could use a script which will be fired as long as the stop-button wasn’t pressed. The script will always finish its run.

Sounds like the rope is a loop, if that’s the case, you only need stops at the motor side. One on each side of the motor pulley drive.

@assembly
I am trying to do it even more concrete.
I plan an iron rope which is fixed on two points. On this rope there is the figure. Something like a blown up ghost or something similar.
On this figure there is a simple string (not a real rope, sorry).
The servo now pulls the string and the figure is moving upwards.
Then theservo pushes the string and the figure is turning downwards by earth gravity with the speed of the servo.

Thank you for your idea with the cover state. I will play around with it. But still wondering why my idea with variables isn’t working at all :frowning:
Your idea with the script is even more genius and I am wondering if I need to track the position at all with this idea as I understood it right.

You’ll here from me, many thanks.

@Mikefila I also thought about rope in a loop. But wanted to try it first with the above described way :wink:

So it is a winch style, where the rope wraps a spool? You could use a limit switch that opens when there is nothing on the spool. Then for the up an endstop like a chainfall uses. The smart tech that they show after is likely a rotary encoder or hall effect type sensor connected to the spool.

@Mikefila

Nice ideas. I will come back to them if the simple solution isn’t working.

@assembly

use a script which will be fired as long as the stop-button wasn’t pressed. The script will always finish its run.

This seems not to be 100% accurate. In my implementation the script always stops immediately after the current step which it is performing. So more or less the same compared to my old implementation

The current script looks like this

script:
  - id: flying_goose
    then:
    - repeat:
        count: 100000000
        then:
          - cover.open: flying
          - delay: 8 sec
          - cover.close: flying
          - delay: 9 sec
                
button:
  - platform: template
    name: "Template Button"
    on_press:
      - script.execute: flying_goose
        

  - platform: template
    name: "Template Button 2"
    on_press:
      - script.stop: flying_goose

I was thinking something along these lines (not tested!):

switch:
  - platform: template
    name: "Flying Goose"
    id: flying_goose_switch
    optimistic: true

script:
  - id: flying_goose
    then:
      - cover.open: flying
      - delay: 8 sec
      - cover.close: flying
      - delay: 9 sec
          
interval:
  - interval: 20 sec
    then:
      if:
        condition:
          switch.is_on: flying_goose_switch
        then:
          - script.execute: flying_goose

The script only runs while the switch “Flying Goose” is “on”.
As soon as you turn this switch off, the script will finish its run and doesn’t start a new run until the switch is turned on again.

You don’t necessarily need to use a script in this case, but I think it makes it a bit easier to read and make it more flexibel to add more functionality.

1 Like

I understand the different approaches we tested now.
In my understanding every of these ways should work as intended ^^
But yeah only your suggestion is working out of the box!!! Thank youuu!

Now I will proceed with developing and printing a case, some gears for faster gear transmition ratio not to strain my servo too much on high speed for hours, as well as a guiding rail so it keeps track during windy times.