WHILE loop acts like an IF statement
-
I have been trying to create a while loop that either breaks out after some time or when an input becomes high.
The following code sort of works,
If the input is high before running, the code goes through the while loop and displays the 'Input Tripped' message as expected.
If the input remains low the iterations/count are reached and breaks out of the while loop displaying the 'Time Out' message as expected.
However if the input goes from low to high during the WHILE loop it does not 'break' out and 'Times out'
It is almost as if the WHILE acts as an IF
I have tried several variations, like 'while true' , using the iterations variable etc but never had any luck achieving an expected result.
var timeOut = 8 ; Set time out in seconds var tooLong = 0 ; Reset var count = 1 ; Counter reset to 1 var trippedInput = 0 ; Reset echo "State of input is " , sensors.gpIn[2].value G4 P2000 ; Pause for 2 seconds to see the input state while sensors.gpIn[2].value == 0 echo "Seconds " , var.count G4 P1000 ; Pause for 1 second set var.count = var.count + 1 if var.count > var.timeOut set var.tooLong = 1 break if var.tooLong == 1 M291 P"Time Out" S2 else M291 P"Input Tripped" S2
I have had a look at other examples and spent several hours, on what I thought should be a simple execution of a WHILE loop but ............
My next idea is to try and structure the code not using any WHILE statement and using only IF statements. Getting clunkier as we go along.
Any ideas would be great.
Thanks Mark
-
@machinemark what firmware version? Standalone mode or SBC mode?
-
Thanks dc42, I see you have been busy in the forums.
echo boards[0].firmwareVersion
Returns 3.5.1
As far as I know, the Duet is in Standalone mode ie we do NOT have an SBC like a RPI or other hanging off the Duet.
Thanks Mark
-
If I understand correctly, the expected behavior when the input goes from low to high during the while loop is that on the next iteration of the loop, when the loop condition is tested (sensors.gpIn[2].value == 0) the loop does not exit.
Is it possible the input is returning to the low state before the condition is checked?
As a debug step, you might add a an echo of the sensors.gpIn[2].value at the end of your loop so that you can see that the value was high at that moment.
If you need to catch a short spike on the input, you might look into is setting up a trigger (https://docs.duet3d.com/en/User_manual/Tuning/Triggers) on the input. The trigger would catch a spike in the input and run a macro that could set a global variable (set global.my_sensor_triggered). Then your code loop above could loop on a number of iterations and break on the global being triggered. Downside of using a trigger is you need to reset the global when you know the sensor is low.
-
Hi mikeabuilder,
That's a top idea. Also modified the echo state during the WHILE loop;
var timeOut = 8 ; Set time out in seconds var tooLong = 0 ; Reset var count = 1 ; Counter reset to 1 var trippedInput = 0 ; Reset echo "State of input before the WHILE loop is " , sensors.gpIn[2].value G4 P2000 ; Pause for 2 seconds to see the input state while sensors.gpIn[2].value == 0 echo var.count , "Seconds. Input state - " , sensors.gpIn[2].value G4 P1000 ; Pause for 1 second set var.count = var.count + 1 if var.count > var.timeOut set var.tooLong = 1 break echo "State of input after the WHILE loop is " , sensors.gpIn[2].value if var.tooLong == 1 M291 P"Time Out" S2 else M291 P"Input Tripped" S2
I notice that the input state only shows '1' if the input is high before starting the macro. If I switch the input high during the WHILE loop, the input state remains at '0' even after the while loop. Unexpected behavior. May need to restructure using IF statement but can not see a way to BRANCH to loop like using a GOTO statement.
Thanks again for the idea, showed something is not right.
-
Interesting. Your results make me wonder if macro execution is "blocking", meaning that the object model is not updated until after the macro finishes. I'm being particular by stating "object model". Your macro is getting the state of the IO from the object model, not a direct reading from the GPIO and if there is some lag in getting the object model updated, it could give the result you are seeing.
It's too bad that M42 cannot be used to return the state of a GPIO. Many other gcode commands return the current value if the value-setting parameter is omitted.
Using a trigger might be needed.
I also wonder about the M950 command you used to define this IO. Maybe different types of IO are handled in different ways when updating the object model.
-
If it's something related to 'while expression', you can test the macro version like:
while true if sensors.gpIn[2].value != 0 break if iterations > var.timeOut set var.tooLong = 1 break
At least, I believe 'while true' is well tested.
-
Hello zuoyan,
Thanks for message, you are spot about macro "blocking". There is a little more to the story. The input is fed from an output (I did this for testing as I am connected to the Duet remotely). So I would run the macro that runs the WHILE loop and as it is counting up I would run another macro to change the state of an output that is connected to the input (sensors.gpIn[2]) that we are testing. Once I got somebody to switch the input with a simple button, all worked as expected.
Thank you for all your help.
-
@mikeabuilder said in WHILE loop acts like an IF statement:
if there is some lag in getting the object model updated, it could give the result you are seeing.
There is no lag in reading OM values within macros when running in standalone mode. Even in SBC mode there is usually no lag because the SBC asks RRF to evaluate expressions.
-
-