Off delay on DI

At 120 fpm, the product is moving at 24 inches / second - so I would want at least 100 msec resolution (2.4 inches of movement). As far as the controller catching .01 second gaps, I would guess that was inconsistent.

That’s where I sit. Just thinking outside the box. I hope Opto realizes these are “work-arounds” not “fixes”.

To get the scan rate of the chart:

In the first block of the chart seed a timer (dt_Chart_X_Scan_Time) with a constant…say 60 seconds.

In the last block of the chart do the calculation: 60 - dt_Chart_X_Scan_Time = f_Chart_X_Scan_Time.

No, it would consistently see the eye turn off then on in 1/2" gaps. Occasionally smaller. (which is when problems occur)
1/2" gap at 120 ft./min. would be ~20 mSec. = 0.02 sec. (we are definitely in the 1/100 sec. range)

In general I need to create somewhere around a 6" gap to give the larger boxes enough room to mesh/merge together, so I need to override gaps smaller than 6" generally speaking.

Thanks! I’ll have to do this. Unfortunately, it will need to be later (I need to wait for some off-time to load the updated strategy). I’ll be back with the results.

You could also use an up timer and skip the subtraction.

Speaking of time… I’m wondering what you mean re:

Entire Strategy loop time is =<60 mSec.

Are you talking about “Comm ‘Loop Time’”? That gives somewhat of an indication of how busy your controller is (and how busy your network is) but NOT how long your whole strategy is taking to execute.

I inserted a “GetRestartTimer” command with an up-timer into the beginning of my chart. After the first loop, it should get the time, put it into a variable, and reset the timer. Yes?

By 60 mSec loop I mean the communication loop listed in Pac Terminal. I realize that is not the time for the strategy to execute (our sucky network sometimes shoots this up into the 200+ mSec range), but it is the only reported time that I can find while running in realtime (not Debug mode).

Okay, I’ve been thinking about this some more and thought that instead of delaying the inputs, you should be able to delay the outputs (and it’s built in!) and have the same type of response.

Here is the chart logic:


The Lane 1 Going conditional block looks like:


Notice I am checking if the lane 1 pop up brake is off OR lane 2 pop up brake is on. This is checking if we either continue to use lane 1 or if we should start lane 1 because lane 2 isn’t open.

The rest of the blocks are one-liners.

Here it is in Optoscript:

if(IsOn(diEye1) and (IsOff(doBrake1) or IsOn(doBrake2))) then
  StartOffPulse(fSwitchOffTimeLimit, doBrake1);
endif
if(IsOn(diEye2) and (IsOff(doBrake2) or IsOn(doBrake1))) then
  StartOffPulse(fSwitchOffTimeLimit, doBrake2);
endif

The fSwitchOffTimeLimit is variable to put the delay you want in.

Truth table*:

[TABLE=“class: grid, width: 600”]
[TR]
[TD]Brake 1[/TD]
[TD]Brake 2[/TD]
[TD]Eye 1[/TD]
[TD]Eye 2[/TD]
[TD]Response[/TD]
[/TR]
[TR]
[TD]Off[/TD]
[TD]On[/TD]
[TD]Off[/TD]
[TD]Off[/TD]
[TD]Do nothing, let the off pulse run[/TD]
[/TR]
[TR]
[TD]Off[/TD]
[TD]On[/TD]
[TD]Off[/TD]
[TD]On[/TD]
[TD]Do nothing, let the off pulse run[/TD]
[/TR]
[TR]
[TD]Off[/TD]
[TD]On[/TD]
[TD]On[/TD]
[TD]Off[/TD]
[TD]Restart Lane 1 brake off pulse[/TD]
[/TR]
[TR]
[TD]Off[/TD]
[TD]On[/TD]
[TD]On[/TD]
[TD]On[/TD]
[TD]Restart Lane 1 brake off pulse[/TD]
[/TR]
[TR]
[TD]On[/TD]
[TD]Off[/TD]
[TD]Off[/TD]
[TD]Off[/TD]
[TD]Do nothing, let the off pulse run[/TD]
[/TR]
[TR]
[TD]On[/TD]
[TD]Off[/TD]
[TD]Off[/TD]
[TD]On[/TD]
[TD]Restart Lane 2 brake off pulse[/TD]
[/TR]
[TR]
[TD]On[/TD]
[TD]Off[/TD]
[TD]On[/TD]
[TD]Off[/TD]
[TD]Do nothing, let the off pulse run[/TD]
[/TR]
[TR]
[TD]On[/TD]
[TD]Off[/TD]
[TD]On[/TD]
[TD]On[/TD]
[TD]Restart Lane 2 brake off pulse[/TD]
[/TR]
[TR]
[TD]On[/TD]
[TD]On[/TD]
[TD]Off[/TD]
[TD]Off[/TD]
[TD]Do nothing, waiting for fruit[/TD]
[/TR]
[TR]
[TD]On[/TD]
[TD]On[/TD]
[TD]Off[/TD]
[TD]On[/TD]
[TD]Start Lane 2 brake off pulse[/TD]
[/TR]
[TR]
[TD]On[/TD]
[TD]On[/TD]
[TD]On[/TD]
[TD]Off[/TD]
[TD]Start Lane 1 brake off pulse[/TD]
[/TR]
[TR]
[TD]On[/TD]
[TD]On[/TD]
[TD]On[/TD]
[TD]On[/TD]
[TD]Start Lane 1 brake off pulse[/TD]
[/TR]
[/TABLE]

*Brakes should start in on position, so both Off is undefined.

I tested this on the bench and it all seemed to work as expected - no fruit involved though.

So, I got the timer uploaded. Seems the chart is running right at 0.1 sec/loop. Sometimes a little slower, sometimes a little faster. So that just barely falls into the resolution I was looking for.

Nice work Philip. I am going to have to experiment with that. I am not sure about a brake always being on. There currently is a state with both off, but never with both on.

I was trying to avoid the situation where Lane 1 is open with nothing coming down and then Lane 2 gets some product. Lane 2 will then have to wait for the off delay on Lane 1. If both brakes are on, then as soon as a product triggers the eye, that brake will be turned off. It can definitely be optimized if needed for the process - not sure if it could still be in six lines of Optoscript though. :wink:

This has been fun, good luck, let us know how it goes!

Regarding timing the chart ‘scan rate’.
I offer the following code (not mine, but it works great).
Drop the code into a block that gets run once per loop of the chart.
(eg, an OptoScript block at the start or end of the main loop you want to time).

//the chart has run through once, so stop the timer
PauseTimer(chart_loop_time);

//keep track of how many times this chart has run through
IncrementVariable(chart_cycle_number);

//keep track of how much time in total this chart has been running
chart_cycle_time_total = chart_cycle_time_total + (1.0 - chart_loop_time);

//display how long in Msec this chart takes to run
chart_cycle_time = chart_cycle_time_total/chart_cycle_number;

//load 1 second back into the timer (assumes the chart will loop in under a second).
chart_loop_time = 1.0;

I have actually found this to work so far, and it’s super simple;

//Chart time loop
powerup_chart_time = GetRestartTimer(Powerup_Chart_timer);

Interesting observation;
When idle (nothing running) the chart takes longer to execute then when the conveyors are running and boxes are going by giving the system events to read/react to. The difference varies between 200-300 mSec.

In the tangent related to tracking chart loop time, I also prefer an up timer, using a structure similar to the following (where the average = value of timer / number of loops). I’m not too interested in the loop time for every loop, just want to make sure the average loop time is good.

Generally, my “Do stuff” will have a few parallel paths that could have different execution times, and if I really want to see every loop, the conditional can be reduced to a very short time interval, with min and max captured in their own variables.


In critical charts, I typically make the loop delays controllable through the HMI. It is fun to balance the delays between charts to make sure those that need to be responsive are, and those that don’t need to be responsive are not. It also becomes an easy way to prove that setting loop delays to the minimum is not always the path to fastest overall execution time.

The cleanest way is to use a timer solution like suggested, the problem with this is it is only as accurate as the scan time which can vary. If the scan time is relatively stable, and is <50 ms, then it may work ok. The events method is by far the most accurate method, but also, the hardest to implement. You can even change the timer value on the fly from the controller, two down sides are figuring out the implementation, and testing it. The third downside is making sure it gets saved in flash, preventing the overwriting in the future. This is the most tricky part, I have been down this road before and the interaction between the controller and the brain when trying to save to flash and so on is not straight forward. That said, this is possibly the most powerful aspect of Opto’s system. It’s too bad they haven’t made it much easier and fool proof to use.

Actually, what we did to make MOST of the mem map easier to use, is to add the flowcharting option into the brain too (SNAP-R1 & -R2 or actually it’s predecessor: “Ultimate I/O” if you remember that…) so you don’t HAVE to worry about all those addresses and whatnot!

However, if you DO want to eeek out a little more speed or functionality by addressing the mem map directly (like I mentioned [U]in this earlier post[/U] in this thread), I recommend NOT doing that from PAC Manager then storing to flash. Rather, do all those config writes in your Powerup chart using the “I/O Unit - Memory Map” commands like “WriteNumberToIoUnitMemMap” so ALL of your logic and config is in your strategy, not all except your special mem map stuff. Also, that way you don’t have to worry about storing it to flash.

I recommend this same: “code it in your strategy” approach for those for you using the built-in serial port on the SNAP-PAC-R series controllers. To use that port in a strategy/chart (vs. default PPP), you COULD change that setting in PAC Manager and store it to flash. But let’s say you want to duplicate, or replace, your R1–it’s easy to forget that PAC Manager step and wonder why you’re getting a -20 error on your comm handle that was working fine before. Better to just code those mem map addresses into the strategy so all you need for your logic is in that one PAC Control archive!

1 Like

Heh… I was revisiting this piece of equipment, trying to figure out how to optimize it (yes, still). I recalled someone mentioning this as an option. Pretty funny to discover it was me! I’m not sure why I never actually tried this, but I just did and it seems to work as an impromptu off-delay.

If (Maf_Merg_Eye1) Then StartOnPulse(Maf_Merge_Eye_Delay, emu_Maf_eye1);
Endif
If (Maf_Merg_Eye2) Then StartOnPulse(Maf_Merge_Eye_Delay, emu_Maf_eye2);
Endif

Interesting.

StartOnPulse does work for an off delay (just like StartOffPulse works for an on delay) - the issue I have with the pulse commands is that I still need to use a flag to know if it has been called or not - and by that time I might as well just use a timer. I know those commands have a status buried in an undocumented area of the memory map, they just aren’t available as a nice command like PulseTimeRemaining(my_do).

In your example, if your Maf_Merg_Eye1 relied on the emu_Maf_eye1 shutting off and your loop was fast enough, the on pulse would just start again.

That’s the idea. This is faking an off-delay for an INPUT. As long as the eye is on, an OnPulse keeps getting fed to the dummy output (resetting the timer) which is what I am now looking at for my input, but when the eye turns off it acts like the physical timer on the eye and turns the dummy output off after the prescribed time limit regardless of where the processor is in the loop. Only this gives me much finer control vs. a tiny dial that you just turn to make it work.

The logic relies on the emu_Maf_eye1 to turn off, which relies on the physical eye to turn off. emu_Maf_eye1 does not physically exist.

This makes for a pretty simple off-delay for an input.

I thought I would share the machine in motion. This isn’t even “high traffic.” Looks simple right?

Box Merger

2 Likes