In this setup a potentiometer sets a 0–5 V starting point. When a switch is flipped, a LabJack D/A output driven by DAQFactory must move from that point to 5 V, linearly, over a time the operator enters. The hardware is a U12, with a UE9 also available. The concept is a timed fader. The working solution is an open-loop interpolation sequence: it latches the pot reading, waits a set delay, then writes start + fraction_elapsed × (5 − start) to the output until the time runs out.
The signal chain has three parts:
- Measured signal: the pot wiper voltage on an analog input channel.
- Controller: a DAQFactory sequence that computes each output value from elapsed system time.
- Final element: the D/A output channel, which is what you put a meter on.
Nothing feeds the output back into the calculation. If the needle does not move, the fault is in the channel, the wiring or the script flow. No gain setting is involved. Work through the checks below in order, because each one assumes the previous one passed.
| Signal | Source in the application | Symptom when the value is wrong |
|---|---|---|
Pot setpoint (Input) |
Analog input channel wired to the pot wiper | Display stays at 0 or does not track the pot: wiring or channel configuration, not script |
Ramp output (Output) |
D/A output channel | C1005 error or dead meter: the output channel was never created |
Var.TotalTime |
Variable Value component, Set To action | Output jumps straight to 5 V when the value is 0 or unset, because the while loop never executes |
Var.UpdateTime |
Variable Value component | Coarse staircase if large; loop hogs CPU if near zero |
Var.InitialVoltage |
Latched from Input[0] at start |
Ramp slope changes mid-run if the pot is read live inside the loop |
Start delay (Var.DelayedStart) |
Variable Value component | Output sits at 0 V through the delay, then jumps, when the delay lives in the channel event |
start / stop switches |
Digital input channels | Ramp starts on the wrong sample when the index is [1] instead of [0]
|
Registry.StartVoltage |
Windows registry | Output powers up at 0 V (registry default), or a fractional value is truncated |
| Graph trace | 2D graph Y expression | No trace, left axis stuck at 70–180: graph frozen or expression returns a scalar |
Is the pot a readable divider, or part of the supply's regulator?
Get the units straight first. A pot on a 5 V supply varies voltage, not current. A current-adjustable supply is a different instrument with different wiring.
Next, find where the pot sits in the circuit. There are three cases, and each points to a different hardware approach.
- Pot is a plain voltage divider on the supply output. A relay can bypass it to force full 5 V. The software can also simply read the wiper.
- Pot adjusts the supply's internal regulation circuitry. It may pull a node toward ground or away from it to raise the output. Overriding it from outside without knowing which way it works can destroy the supply.
- Pot is added downstream of a fixed 5 V supply. This is the cleanest architecture.
The downstream layout works like this:
- Leave the supply fixed at 5 V.
- Put your own pot downstream as a divider. Its ends go to 5 V and common, and the wiper goes to an analog input channel.
- Generate the ramp on the LabJack D/A output, not on the supply.
With this layout the pot is purely a measured setpoint and the D/A output is the final element. Nothing in software has to reach inside the supply.
Reading to take: put a meter on the wiper while turning the pot, then compare it with the input channel value in DAQFactory's channel table.
- Both track 0–5 V: go to the next check.
- The meter tracks but DAQFactory does not: the fault is the input channel. Check the device type, channel number and that the channel was actually created. Fix that before touching any sequence.
- The meter does not track: it is a wiring fault.
Does the D/A channel exist and hold a fixed value on a meter?
A C1005 error on even trivial scripts in this application came from referencing an output that did not exist yet. Both the D/A output channel and the startup sequence had been forgotten. DAQFactory scripts can only assign to channels and variables that have been declared. Writing Output = 3 before an output channel named Output exists fails at run time.
- Open the channel table and add a D/A output channel on the device you are using (U12, UE9 or U3). Name it
Outputso the scripts below run unchanged. - If the hardware is not connected yet, create
Outputas a test channel. This proves the script logic without the DAC. Swap in the real device type when the hardware is ready. - Create a startup sequence that writes a known value, for example
Output = 0. Mark it to run at application start. - Put a meter on the D/A terminal. Write a fixed value such as 2.5 from the command line or a screen control, and read the meter.
The outcome decides the next step:
- The meter reads the written value: the output path is proven, so any later ramp failure is script logic.
- The meter reads nothing: recheck the channel's device and channel number and the physical terminal.
- The meter tops out below 5 V: read the DAC output range in the device datasheet. The target must lie inside the analog output span.
Why does counting loop passes not give a timed ramp?
The first attempt used a polling loop:
- Read the input.
- If it was at or below 5, execute
Digout +1. - Wait one second, then repeat.
This fails in four separate ways:
- DAQFactory has no
thenkeyword.if (condition)is followed directly by the statement block and closed withendif. -
(Digout + 1)is an expression with no destination. Valid forms areDigout = Digout + 1orDigout = 1. -
wait()is rarely the right call. Usedelay()for loop pacing in sequences. - Monitoring an input by polling it in a sequence duplicates work the channel already does. Input monitoring belongs in the channel's event, which runs on every new reading.
The corrected syntax looks like this:
while (1)
read(inputvalue)
if (inputvalue <= 5)
Digout = 1
endif
delay(1)
endwhile
The deeper problem is architectural. An increment-per-pass ramp has no time base. Suppose you define 1000 passes as 5 minutes. The real duration is 1000 × (delay + script execution + I/O time). That drifts with PC load and grows with every extra line of code.
The step size is also fixed. A pot set at 1 V takes longer to reach 5 V than one set at 4 V, so the operator's time setting means nothing. A fader must hit the endpoint at the requested time regardless of where it started. That requires computing the output from elapsed time, not from a pass counter.
How does the sequence compute each output value from elapsed time?
The rise sequence records the start time once. On every pass it computes how far through the transition it is, then writes the interpolated value:
Var.TotalTime = 300 // total change time in seconds
Var.UpdateTime = 1 // how often to change output
Var.InitialVoltage = 1 // starting voltage
Var.EndVoltage = 5 // ending voltage
// the four values above can be set from screen components instead
private.starttime = SysTime()
while (SysTime() < private.starttime + Var.TotalTime)
Output = Var.InitialVoltage + (SysTime() - private.starttime) / Var.TotalTime * (Var.EndVoltage - Var.InitialVoltage)
delay(Var.UpdateTime)
endwhile
Output = Var.EndVoltage // last step can be missed inside the loop
The formula works in two stages:
-
(SysTime() - private.starttime) / Var.TotalTimeis a fraction that runs from 0 at the start to 1 when the total time has elapsed. - That fraction is multiplied by the total change
(EndVoltage - InitialVoltage)and added to the starting voltage.
Because each write is derived from the clock, a late pass does not accumulate error. It just writes a slightly larger value.
The final assignment after endwhile is mandatory, not cosmetic. The loop exits as soon as the elapsed time passes TotalTime. The last in-loop write therefore happened up to one UpdateTime earlier, at a value just below 5 V. Without the closing write the output parks short of the target.
Step size, derived from the evidence values. Take a 3 V pot setting, a 5 minute ramp (TotalTime = 300) and UpdateTime = 1. The output changes by (5 − 3) / 300 ≈ 6.7 mV per update.
Compare that step with the DAC's resolution from the device datasheet:
- Step smaller than one DAC count: the meter shows a staircase with several updates per visible step. That is correct behavior, not a fault.
-
Shorter
UpdateTime: the ramp is smoother, but only down to the DAC resolution. -
Longer
UpdateTime: the steps are coarser, but the endpoint time does not change.
The same formula ramps downward if EndVoltage is below InitialVoltage. With a pot already at 5 V the change term is zero and the output simply holds.
Where do the ramp time, update rate and start point get their values?
Scope decides where each variable lives:
-
private.The variable exists only inside the sequence that declares it. Use it for working values nothing else touches, such asprivate.starttime. This keeps the global namespace uncluttered. -
Var.The variable is global. Use it for anything a screen control or another sequence must change: total time, update interval, end voltage and start delay.
A Var. variable can be used anywhere an output channel can. To make one operator-settable:
- Place a Variable Value component on the page.
- Open its properties and click the Analog Out button at the bottom right.
- Enter the variable name, for example
Var.TotalTime. The button sets the component's Action tab to Set To with that variable as the target. - At run time, clicking the component prompts for a new value and writes it to the variable.
- Repeat for
Var.UpdateTime,Var.EndVoltageandVar.DelayedStart.
Remove the matching hard-coded assignments from the top of the rise sequence. Otherwise each run overwrites the operator's entry. Give each variable a sane default in the startup sequence instead. An unset Var.TotalTime of 0 makes the while condition false on the first test, so the output jumps straight to 5 V.
For the start point, replace the fixed Var.InitialVoltage with the pot reading. Latch it once, before the loop:
Var.InitialVoltage = Input[0]
Latching matters. If Input[0] appeared directly inside the formula, turning the pot mid-ramp would move the line's origin and bend the ramp.
Why does the output sit at 0 V through the start delay and then jump?
The next requirement was separate start and stop switches. When start is flipped, the output should go to the pot value, hold for a set number of seconds, then begin rising. The first attempt put the delay in the switch's channel event:
if (start[0] == 1 && stop[1] == 0)
delay(var.DelayedStart)
beginseq(rise)
endif
The result was 0 V for the whole delay, then a jump to the start voltage and the rise. Adding input = output in front of the delay changed nothing. That line also has its assignment backwards: it writes the output's value into the input channel, not the pot value into the output.
The mechanism is how events run. A channel event executes inside that channel's acquisition loop. A delay() there stalls the polling loop, so new readings stop arriving and nothing downstream updates until the delay ends. Moving the delay inside the while loop is no better, because it would pause every pass of the ramp. break and continue are not the fix either: the delay belongs in a different place, not a different loop.
Keep the event to a pure trigger:
if ((start[0] == 1) && (stop[0] == 0))
beginseq(rise)
endif
Then do the hold inside the rise sequence, which runs in its own thread:
Output = Input[0] // jump output to the pot setting immediately
Var.InitialVoltage = Input[0]
delay(Var.DelayedStart) // hold before rising
private.starttime = SysTime()
while (SysTime() < private.starttime + Var.TotalTime)
Output = Var.InitialVoltage + (SysTime() - private.starttime) / Var.TotalTime * (Var.EndVoltage - Var.InitialVoltage)
delay(Var.UpdateTime)
endwhile
Output = Var.EndVoltage
Set private.starttime after the hold delay. If it were recorded before, the hold time would be counted as ramp time.
Parentheses. Wrap each comparison in its own parentheses. It evaluates the same here, but it reads clearly and removes any dependence on operator precedence.
Indexes. [0] is the most recent reading, while [1] is the reading before it. The original condition tested stop[1], which is the stop switch's previous sample, not its current state. Use stop[0] unless you are deliberately detecting an edge.
The same rule applies to Input. Without an index, a channel name returns its entire history. Output = happens to trim that to the newest value, but relying on it causes three problems:
- The intent is unclear to the next reader.
- DAQFactory retrieves the whole history before trimming, which is expensive with long histories.
- Not every context trims automatically, so results can differ from what you expect.
Always write Input[0].
For the stop switch, give it its own event that ends the rise sequence. Then write whatever safe value the process needs to Output. Without that, the rise sequence keeps running to 5 V after stop is flipped.
What voltage should the output hold when DAQFactory starts?
The design has two different "initial voltages":
-
Power-up value. This is what the startup sequence writes to
Outputwhen the application launches. -
Ramp origin. This is
Var.InitialVoltage, latched from the pot each time the rise sequence starts.
They do not conflict. The power-up value holds until the first start command, and the rise sequence then overwrites the output with the pot reading.
To make the power-up value operator-settable and keep it across restarts, use a registry variable. It is stored in the Windows registry rather than in memory, so it survives a PC reboot. In the startup sequence, replace Output = 0 with Output = Registry.StartVoltage. Then add a Variable Value component that sets Registry.StartVoltage. After changing it and restarting, the output comes up at the stored value.
Registry variables have two limits:
- They default to 0. A fresh install therefore powers the output up at 0 V.
- They hold only integers and strings, so a value such as 1.34 V cannot be stored directly.
Add a scaling multiplier to handle fractions. The following is an assumed scaling of millivolts:
Output = Registry.StartVoltage / 1000 // registry holds mV, e.g. 1340 for 1.34 V
The operator's entry component must then take millivolts. Alternatively, have a small sequence multiply the entered voltage by 1000 and round it before storing.
Why is the trend graph frozen on a 70–180 axis, and what else does the U12 not do?
A 2D graph that stopped showing traces and kept a left axis of 70 to 180 has two usual causes.
The graph is frozen. A frozen graph shows a purple box around it when clicked.
- Right-click the graph and select Freeze/Thaw.
- Choose Thaw all axes.
- Left-click the graph again. A thawed graph shows green lines at the top and bottom.
- Try autoscaling the Y axis. If the scale changes, the axis is live.
The Y expression has nothing to scale. If the Y expression returns no data, or only a scalar, the graph has no series to fit and the axis scales oddly. Open the trace properties and read the Y expression. It should name the channel's history, for example Output with no index, so there is a time series to plot. Output[0] is a single scalar.
Standalone operation. None of the LabJack units (U12, UE9 or U3) can run a program on their own. The ramp logic lives in DAQFactory on the PC, so the PC must stay running for the whole ramp. If the application closes mid-ramp, the D/A output keeps its last written value.
Gauges. Angular gauges are not included in DAQFactory Express; you need at least the DAQFactory Lite tier. A gauge can display any scalar. For elapsed ramp time, write the elapsed seconds to a Var. variable inside the loop and point the gauge at it.
What is the full build-and-verify procedure for the rise sequence?
- Wire the pot as a divider from a fixed 5 V supply to common, with the wiper on an analog input. Confirm with a meter that the
Inputchannel tracks the wiper across 0–5 V. - Create the
OutputD/A channel on the correct device. Write 2.5 manually and confirm 2.5 V on a meter at the terminal. Any C1005 error at this stage means a referenced channel is missing. - Create digital input channels
startandstop. Toggle each switch and confirm that its[0]value changes in the channel table. - Write the startup sequence:
- Initialize
Var.TotalTime,Var.UpdateTime,Var.EndVoltage = 5andVar.DelayedStartto non-zero defaults. - Write
Output = Registry.StartVoltage, scaled if you store millivolts.
- Initialize
- Add Variable Value components with the Set To action for each
Var.parameter and forRegistry.StartVoltage. - Write the rise sequence in this order:
- Latch
Input[0]into bothOutputandVar.InitialVoltage. - Run
delay(Var.DelayedStart). - Record
private.starttime. - Run the interpolation while loop.
- Write
Output = Var.EndVoltageafter the loop.
- Latch
- Put only the trigger
if ((start[0] == 1) && (stop[0] == 0)) beginseq(rise) endifin the start channel's event. Do not put any delay there. - Give the stop switch's event the job of ending the rise sequence and writing the safe output value.
- Add a 2D graph with
OutputandInputas Y expressions. Confirm it is thawed and scrolling.
Verify on the meter, not just on screen:
- Set the pot to about 3 V,
Var.TotalTimeto 60 andVar.DelayedStartto 5. - It should then climb about 33 mV per second with a 1 s update: (5 − 3) / 60 ≈ 0.033 V/s. Time it with a stopwatch.
- Repeat from about 1 V. The endpoint time must be the same, with only the slope different.
- Flip stop mid-ramp. The output must stop rising at once.
- Restart DAQFactory and confirm the output powers up at the registry value.
If the endpoint lands at 5 V on time from any start point, the timing is correct and the fader is done.
Frequently asked questions
What happens if I put delay() inside a DAQFactory channel event?
The event runs in the channel's acquisition loop, so the delay stalls polling for its full duration. Readings stop updating and the output sits at its old value, then jumps when the delay ends. Keep the event to a beginseq() trigger and put the delay inside the sequence.
What happens if Var.TotalTime is 0 or never set?
The condition SysTime() < starttime + TotalTime is false on the first test, so the loop body never runs. The final Output = Var.EndVoltage then steps the output straight to 5 V. Initialize it to a non-zero default in the startup sequence.
What happens if I use Input without [0] in a DAQFactory sequence?
DAQFactory returns the channel's entire history and trims it only when the assignment takes the newest value, which wastes memory and time on long histories. In contexts that do not trim automatically, you get an array instead of a scalar. Write Input[0] explicitly.
What happens if the pot is turned while the output is ramping?
If Var.InitialVoltage was latched from Input[0] before the loop, nothing changes and the ramp continues on its original line. If Input[0] sits directly inside the interpolation formula, the ramp's origin moves every pass and the slope bends. Always latch the start value once.
What happens if the ramp still fails after all these checks?
If the meter confirms the D/A output follows manual writes, but the sequence still throws errors or the timing drifts beyond one update interval, stop editing the script. Export the channel table and sequence and take them to AzeoTech's official DAQFactory support channel. For output range, DAC resolution or device detection problems on the U12, UE9 or U3, contact LabJack's official support with the device model and the meter readings.