Q-script is the scripting language for
the QTU (Quad Throttle Unit). Once you become familiar with the
scripting language you can write your own program to control your
system in anyway that you desire.
Q-script includes pre-defined
functions and variables that you will use to create your own
functionality.
The first few examples will help you understand
the pre-defined functions of Q-script and thereby minimise the amount
of script that you will need to create.
If you have a QTU and have downloaded
Tcc, you will be able to compile, download and test each of the
programmes on your own hardware as we go along. Once you have
downloaded your programme the QTU can run your layout autonomously
without a computer connected.
(For instructions on downloading Tcc
see XXX)
There are 4 throttles on a QTU. Each is given an identity 0 to 3.
We will just use throttle 0 for now.
Lets connect the throttle
output of throttle 0 to a circle of track, and connect a speed
control to the first analogue input.
Each analogue input has a
name AdcUser 0 to 7.
|
Item |
Input /Output on QTU |
Q-Script name |
|
Speed Knob |
1st analogue input |
AdcUser0 |
Note : Take care to check the position of the input carefully. Each connector port also has power and ground pins and so the first input pin will NOT be the first pin on the connector.
Q-script has a predefined variable
speed for each throttle. The
variable for throttle 0 is speed0
The
script to set the throttle output to be controlled by the speed knob
follows.
|
speed0 = AdcUser0; |
Enter
this into Tcc - QTU script, compile and download. Now as you vary the
speed knob, the speed of the train will vary.
The end
statement tells the Q-script that
you have finished, and gives your script a name "Speed_example".
Just a speed control might stunt your style a little, so lets add 2 new inputs Brake and Reverse. These are ON/OFF switches and so are connected to General Purpose Inputs. These lines are know by the Q-script as input0 to input31. In scripting terms these are binary inputs that can have the value of 0 or 1.
|
Item |
Input /Output on QTU |
Q-Script name |
|
Speed Knob |
1st analogue input |
AdcUser0 |
|
Brake switch |
1st input (General Purpose) |
input0 |
|
Direction switch |
2nd input |
input1 |
Like speed, Q-script has pre-defined variables for brake and reverse. So we will set these for throtle0.
|
speed0 = AdcUser0; |
Note carefully the
NOT symbol which is the ! in the equation for the brake and
reverse.
Binary input lines are pulled high, so when the brake
switch is applied the line is pulled down to ground.
In our script
we want the throttle brake to be applied with value 1 when the input
switch is low.
Running this
script on your QTU, first try applying the brake.
We have told the
QTU that it should use input0 as the brake. Therefore when the brake
is applied and the input line goes to zero.
The built-in brake
function within the QTU understands, and will apply the brake.
When
the brake is removed, the speed of the train will be returned to the
speed setting.
Now try applying
reverse. When reverse is applied, the built-in QTU functions again
provide the required functionality.
The train is stopped and will
then start again in the reverse direction returning to the required
speed setting.
So the passengers were turned
into pancakes when we activated the reversing switch, perhaps the
train should have some inertia or momentum.
So far we have used
the predefined items: speed,
brake and reverse.
Let's use two more :- inertia and
feedback.
Lets add
a new switch called feedback_control to turn ON or OFF feedback, and
a new analogue knob to set the inertia.
|
Item |
Input /Output on QTU |
Q-Script name |
|
Speed Knob |
1st analogue input |
AdcUser0 |
|
Brake switch |
1st input (General Purpose) |
input0 |
|
Direction switch |
2nd input |
input1 |
|
Inertia Knob |
2nd analogue input |
AdcUser1 |
|
Feedback switch |
3rd input |
input2 |
Again we need to tell the Q-script what inputs are to be used.
|
speed0 = AdcUser0; |
So compiling the
script and downloading once again, what do inertia and feedback
provide?
Inertia is the term used to describe how quickly a speed
change takes effect on the train.
In previous examples, when the
brake was applied the train was stopped immediately and rather
clumsily.
With inertia being applied, the braking function
provided by QTU is modified to bring the train to a more gentle stop
(dependant upon the inertia setting).
Like inertia, feedback has a modifying effect on how the trains are run by the QTU. If you have gradients or sharp curves in your layout, you may well notice that the speed of the train changes as it traverses the layout. If you switch on feedback, the QTU then uses feedback from the train to monitor the speed, and will adjust the voltage so that a constant speed is maintained.
So far we have only used the first throttle - throttle0. So lets
expand our system to have 4 throttles each running a train on an
independent loop.
So we wire up the 4 throttles and then once
again tell the QTU how the system is wired up.
To make our code
more readable we will add some equate statements (equ) so
that we can use more readable names
|
Throttle Number |
Item |
Input /Output on QTU |
Q-Script name |
|
0 |
Speed Knob |
1st analogue input |
AdcUser0 |
|
0 |
Brake switch |
1st input (General Purpose) |
input0 |
|
0 |
Direction switch |
2nd input |
input1 |
|
|
Inertia Knob |
2nd analogue input |
AdcUser1 |
|
|
Feedback switch |
3rd input |
input2 |
|
1 |
Speed Knob |
3rd analogue input |
AdcUser2 |
|
1 |
Brake switch |
4th input (General Purpose) |
input3 |
|
1 |
Direction switch |
5th input |
input4 |
equ Inertia_Setting = AdcUser1;
inertia0 = Inertia_Setting;
inertia1 = Inertia_Setting;
speed0 = AdcUser0;
brake0 = !input0;
reverse0 = !input1;
feedback0 = !input2;
speed1 = AdcUser2;
brake1 = !input3;
reverse1 = !input4;
//ADD THE EQUATIONS FOR TROTTLE 2 AND 3 HERE
end "QTU_example_4"
|
We have added throttle1 to the
connection table and the code. You should add the connection
information for throttles 2 & 3 and then add the new equations
where the comment line in the code indicates.
Note that all lines
starting with //
are comment lines and that comments may also be put at the end of
lines using the // marker.
Also note that we have decided not to
add another inertia input for throttle1. We have used the same
inertia input as throttle0.
(Assuming that your engines have
similar characteristics and will slow down in a similar fashion -
then we don't really need an inertia setting per loop.)
After
completing the code for all 4 throttles you should have "manual
control" over your each train on each of your 4 loops.
So
most of the "work" has so far been done for you. Lets
actually write a script to do something different: A script to stop
each train in turn and change it's direction with about 1 minute
between trains.
In order to keep track of the time, we will need a
counter. This needs to be a byte (analogue) variable ie. a variable
with a range 0- 255.
The general purpose analogue variables
available to us in Q-script are called ByteMemory
There
are 14 of these general purpose variables, the first being
ByteMemory0 and the last ByteMemory13
So lets make a
half_minute_counter using ByteMemory8.
equ half_minute_counter = ByteMemory8; |
We
are also going to use some more Q-script function which uses the
specialised variables: Duration and
period.
Duration
sets how long a time delay should
last in the range 0 to 255 and in units of 100ms, so the maximum time
is 255 * 100ms = 25.5 seconds.
period is
a flag which if set to 1 (TRUE) will be cleared to 0 (FALSE) after
Duration expires.
There
are 4 Duration/ Period pairs available. For this example we will use
the pair Duration0 and Period0.
(Note: that the number associated
with Duration, period or ByteMemory does not equate to a throttle
number. You can use any of these as you wish)
equ half_minute_counter = ByteMemory8; // Create minute_counter from general purpose memory
Duration0 = 255; // This gives a time of 255*100ms = 25.5s (close enough to a half_minute)
if (!period0) { // Period0 flag is FALSE showing that duration has expired
period0 = TRUE; // Restart the timer
half_minute_counter += 1; // Increment the half_minute_counter
if (half_minute_counter == 2) { reverse0 = !reverse0; } // After 1 min change throttle0 direction
if (half_minute_counter == 4) { reverse1 = !reverse1; }
if (half_minute_counter == 6) { reverse2 = !reverse2; }
if (half_minute_counter == 8) {
reverse3 = !reverse3;
half_minute_counter = 0; } //Reset the half_minute counter
}
end "Change_Train_Direction"
|
Add the above code
to your code in Example4.
A number of new code constructs are
used in this example which we try to outline below.
|
Code Item |
Explanation |
|
equ a=b; |
equate the name 'a' with the Q-script variable 'b' |
|
a = b |
Binary or analogue variable 'a' is set to be the same as the value of 'b' |
|
a == b |
'a' is compared with 'b' to see if they are the same value |
|
TRUE FALSE |
The binary values TRUE and FALSE are defined by Q-script. They can be used to set binary variables |
|
! |
For a binary item which can have
the values TRUE or FALSE, then the ! (NOT) sign means the
inverse. |
|
if (test) { ...... } |
if the result of the test is true
then execute one or more statements that are inside the
brackets {} |
In example 4 we controlled each of our 4 trains individually. Lets now add a master brake switch that will stop all trains at once.
We will give names to all the brake inputs as well as our new master brake switch:
equ Manual_brake_0 = !input0; equ Manual_brake_1 = !input3; equ Manual_brake_2 = !input5 equ Manual_brake_3 = !input7; equ Master_brake = !input10; equ Emergency_brake = !input11; brake0 = Manual_brake_0 | Master_brake; brake1 = Manual_brake_1 | Master_brake; brake2 = Manual_brake_2 | Master_brake; brake3 = Manual_brake_3 | Master_brake; end "QTU_example_6" |
The brake on each throttle is now controlled by either the manual brake and the master brake: The '|' symbol means 'OR'. So if the manual brake OR the Master brake are activated then the brake is applied.
The master brake added in example 6 will stop all the trains using the inertia setting (if any) to bring the trains to a gradual stop.
Now lets add an emergency stop function. When an individual train brake is applied OR the master brake is applied, we want the inertia setting to be used. However for an emergency stop function we want to override the inertia setting. and bring all the trains to an immediate stop.
Code example given for throttle0 with emergency brake
equ Manual_brake_0 = !input0;
equ Inertia_Setting = AdcUser0;
equ Master_brake = !input10;
equ Emergency_brake = !input11;
if (Emergency_brake) {
inertia0 =FALSE; //Turn off inertia
}
else {
inertia0 = Inertia_Setting; //Turn inertia to users settings
}
brake0 = Manual_brake_1 | Master_brake | Emergency_brake;
end "QTU_example_7"
|
Previously we had set inertia0 directly to the analogue input AdcUser0. For normal running we want to use inertia, but for the emergency stop we want to turn it off. Therefore we now need two inertia settings :- the manual setting this time given the name Inertia_Setting which is read from port AdcUser0 and the throttle inertia - inertia0 which will be set as required.
So far we have had independent loops of track each running a single train.
Lets now change to have a loop consisting of 4 sections. Each section is connected to a QTU throttle. So we want to detect where the train is and apply current to that section to drive the train forward.
NOTE : When using multiple QTU throttles to run sections of track, each section needs to be isolated on both rails from the section.
We do not use a common return line.
So having connected the sections of track, how will we know on which section of track the engine is placed.
The QTU provides current detection circuitry and can detect a train on the track.
The current detection works even when the train is stopped by using very low currents.
Q-script holds a 'state' variable for
each throttle which includes a occupied and overload indicator.
So
for throttle0, state0.occupied indicates whether the section
throttle 0 is connected to is occupied by a train
state0.overload indicates whether there is an overload condition (normally caused by a derailment, a short or perhaps a stalled loco)
So lets say we want some LEDs to show if there is an overload on the system, and where is the overload has occurred.
|
Item |
Input /Output on QTU |
Q-Script name |
|
Current Overload lamp |
1st output (General Purpose) |
output0 |
equ Current_overload = output4; |
As mentioned in example 8, the state variable for a throttle
includes an occupied indicator eg. state0.occupied
So
with 4 sections in a loop, we want to see if the section ahead is
clear before moving a train forward.
|
Item |
Input /Output on QTU |
Q-Script name |
|
Current detect lamp |
1st output (General Purpose) |
output0 |
|
Current Overload lamp |
2nd output |
output0 |
brake0 = state1.occupied; //track ahead is occupied brake1 = state2.occupied; brake2 = state3.occupied; brake3 = state0.occupied; end "QTU_example_7" |