miniThrottle: A model railroad WiFi throttle.
miniThrottle can operate quite successfully without making use of this feature. If you do not understand this section, you probably do not need it and can safely ignore it. Automation scripts are stored as local files on the miniThrottle and can simulate keypad button pushes, check for conditions and to a limited extent interact with local I/O pins. In short it is a programming language allowing relatively easy additional customisation. However, it remains an abstract construct, and the code does not make for easy reading.
Automation scripting was introduced from version 0.7e. Its initial primary purpose was to improve testing of the software. However, it may be useful in setting up the state of a throttle on startup, eg for serially connected devices or relays. It allows customised hardware, eg using a 3V voltmeter to show "water level" or "engine temperature".
NB: The automation script is interpreted at run time and has minimal optimisation. It is not compiled and makes use of minimal tokenisation. It is inefficient. If you are writing some code that will demand lots of cold cranking cpu cycles, this is not a recommended option to use.
Automation script commands don't work at the command prompt. They run from a file. It is also worth noting automation scripts won't run configuration commands. Most notably customised I/O pins setup and interaction is done via automation script only. The command prompt won't directly interact with them.
When interacting with I/O pins, you can do so only with ones you've defined with a configpin instruction. Pins defined in your build to drive the display, keypad and encoder are considered reserved and cannot be read or set through the automation script. It is also assumed you know what you are doing with your customisation. Using PWM to control the brightness of a LED will work, but using it to control a relay is likely to be less than successful.
Automation can be controlled from the command prompt. An automation called "/auto.run" is automatically run at system start up. Five console commands allow control of automation scripts:
Command | Description |
---|---|
run <File-Name> | Will run the specified script as an automation. Include the leading "/" of the file name. |
kill <Proc-ID> | Will stop the automation on completion of the current operation. That is if it is sleeping for an hour and 5 minutes of that has elapsed, the automation will stop in 55 minutes. |
procs | List running automations. Note the process IDs are incremental but not necessarily sequential. They are unique. |
rpn <Expression> | Tests an expression such as those used for conditional goto and wait for. |
trace <Proc-ID> | Turns on/off tracing of the automation script. Note that the line number count on the left of the trace output does not count blank lines. Instead of a blank line, lines with just # on them will help align the trace with actual line numbers. |
Within the script the controller can evaluate RPN style expressions to evaluate various things at run time. Think of the RPN expression as a rudimentary calculator rather than computer! Look up values or constants get pushed on to a stack and single character operators do something to them. These operations are not shown during the running of a script but are displayed when running "rpn". If the result of the rpn is greater than 0.5 the waitfor ends, or the goto is taken. All numbers are treated as floating point digits. At its simplest the expression might just be a single value, like "trackpower". The maximum stack depth of the calculator is 10 digits.
The following example gets a random number and multiplies it by 1000. Then puts 12 and 4 on the stack multiplies them, the result of which is subtracted from the pravious number. And finally checks if the result is less than 500. In this case it is not. If it were in a waitfor, we continue to wait, if it were in a go to we'd not take the goto. Note the "action" follows a left to right execution and at the end there should be one value left on the stack - the result.
rpn random 1000 * 12 4 * - 500 < Op Older <-- Stack --> Newer # 0.8439 . 0.8439 1000.0000 * 843.8519 . 843.8519 12.0000 . 843.8519 12.0000 4.0000 * 843.8519 48.0000 - 795.8519 . 795.8519 500.0000 < 0.0000
Effectively it has checked: "Is ((random * 1000) - (12 * 4)) < 500?" A leading "#" indicates a look-up operation, and a leading "." indicates we've pushed a constant on the stack. Otherwise the caharacter in the Op column indicates the operation performed to get to this stage.
So what would that look like if rpn expressions were put into a simple control script?
# wait for loco selection waitfor driving delay 1000 key L delay 1200 key 0 sleep 1 # bidirectional mode can reverse direction when slowing, use std mode goto :increase bidirectional 0 = key * sleep 1 # Now driving automatically :increase # goto :end if no longer in driving mode goto :end driving 0 = key U sleep 1 # go to :increase until we've reached 90, assuming a 128 step config goto :increase leadloco.speed 90 < sleep 20 :decrease goto :end driving 0 = key D sleep 1 # go to decrease while loco speed is > 0 goto :decrease leadloco.speed 0 > sleep 20 goto :increase :end rem driving program ends exit
Command | Description |
---|---|
# | Lines starting with a # are comments, and may be useful when reviewing the automation at a later date. They can also be used to prevent some action being taken without having to delete the code. eg:# This should do something # exit |
:label | A word proceeded by a colon is a label, together with a "goto" this allows loops to be created. eg::loop key U sleep 20 key D sleep 20 goto :loop |
close <turnout-name> throw <turnout-name> | Set the named turnout to the appropriate position. Also see the "route" command. eg:throw mill-siding-1 close mill-siding-2 |
configpin <pin> <type> [<val>] | Configures a local I/O pin, where pin is the gpio number on Esp32.
For output pins an optional value can be set for initial setting, or if missing 0 is used.
The value may be a rpn expression.
types may be one of:
|
delay <millisecs> sleep <seconds> | Pauses execution for a period of time either 1/1000s of a second or for a full second.
The time parameter can also be an expression, for example to wait between 10 and 30 seconds:sleep 10 20 random * + |
exit | Stops the automation running further. eg:rem last line of useful code exit rem some code we don't want to run |
goto <label> goto <label> <Expr> | Jumps to the named label instead of continuing to the next line of code. |
key <value> | Simulates a key press. Typically the U(p), D(own), L(eft), R(ight) etc keys will be in capital letters. The hardware page lists the useful keys in the "Function" column of the keypad section. |
power [on|off] | Turns track power on or off. |
rem <remark> | Writes a remark to the console. |
runbg <file-name> runfg <file-name> | Runs another automation script, runbg runs it in the background without waiting for completion, while runfg runs it in the foreground and waits for the new automation to complete before continuing. |
route <route-name> | Enable the named route. Also see the "close" and "throw" commands. eg:route wharf2mill |
sendcmd <raw-command> | Sends the command specified to the command station. Does not wait for a return before continuing. The command format must match the selected DCC-EX or WiThrottle format. Can be used to control otherwise unsupported features such as turntables or traversers.. |
set localpin.<pin> <Expr> | Sets an output pin to a value. If the pin connects to a NeoPixel device, the Expr should be 3 values for Red, Green and Blue components. |
set [sh]reg<0-9> <Expr> | Sets a value in a local (private) or shared register.
Local registers are accessible by the script only.
Shared registers can be queried with the rpn command or used by other scripts.
There are 10 registers of each kind labeled between 0 and 9.
Shared registers may be viewed at the command prompt using "dump registers".
They may also be set at command prompt using a set command.
For example:# take the current loco to between 25% and 100% speed waitfor driving set reg0 leadloco.steps 2 - 4 / set reg1 reg0 reg0 random 3 * * + :loop key U delay 500 goto :loop leadloco.speed reg1 < |
waitfor <Expr> | Waits for an expression to be greater than 0.5 before continuing.
The expression may be a simple event such as:
waitfor connected delay 1000 power on waitfor trackpower route platform-1 |
Note: if a lookup name is unknown, then no error is given but zero is returned. Lookup names are as follows:
Name | Description |
---|---|
accesspoint | 1 if Access Point enabled, 0 otherwise |
bidirectional | 1 if bidirectional mode enabled, 0 otherwise |
blankingtime | The time in minutes before the screen blanks, 0 means never. |
connected | 1 if wifi network is connected, 0 otherwise |
cpufreq | Processor frequency in MHz |
dccsensor.<id> | State of the DCC-EX sensor with the specified ID. Always expected to return 0 if running in WiThrottle mode. |
driving | 1 if controlling a locomotive (driving), 0 otherwise |
e | 2.7183 |
fontheight | The height of the font in pixels. |
fontwidth | The width of the font in pixels. miniThrottle uses fixed width fonts. |
freemem | Free memory in bytes |
g | Gravitational constant in metres per second per second |
leadloco | If driving the index number of the lead loco, 0 otherwise |
leadloco.direction | Direction indicator of the lead loco |
leadloco.function | Logically or'd value of functions set |
leadloco.function.<0-28> | 1 if function number is set, 0 otherwise |
leadloco.id | If driving the DCC address of the lead loco |
leadloco.speed | If driving the speed of the lead loco |
leadloco.steps | If driving the step count of the lead loco |
localpin.<pin> | Read local digital or analog pin |
lococount | The number of locomotives in the roster |
memsize | The total (used + free) memory in bytes |
minfree | The minimum amount of memory that has been available in bytes |
pi | The value of pi: 3.1415928 |
potenabled | 1 if driving and potentiometer enabled, 0 otherwise |
random | A random value between 0.00 and 1.00 |
reg<0-9> shreg<0-9> | local (private) registers and shared registers |
routecount | The count of routes |
routestatecount | The number of states routes can be in |
screenheight | The height of the display in pixels |
screenwidth | The width of the display in pixels |
trackpower | 1 if track has power, 0 otherwise |
turnoutcount | The count of turnouts |
turnoutstatecount | The number of states a turnout can have |
uptime | The number of minutes since the throttle was started |
xtalfreq | The processor crystal frequency in MHz |
Note that miniThrottle's operators are all single a character in length. This eases implementation, but means some operators may be harder to remember, or are potentially confusing, eg Capital I for Integer-round-up vs lowercase L for Logarithm.
Operator | Description |
---|---|
+ | add last and prior numbers on the stack |
* | multiply last and prior numbers on the stack |
- | subtract the last number on the stack from the prior one |
/ | divide the prior number on the stack by the last one |
% | modulus division of the prior number on the stack by the last one |
^ | raise the prior number on the stack to the power of the last number on the stack |
! | inverse of the last number on the stack, 1/n |
& | returns 1 if both prior AND last numbers on the stack are greater than 1, otherwise 0 |
| | returns 1 if either of prior OR last numbers on the stack are greater than 1, otherwise 0 |
= | returns 1 if both prior and last numbers on the stack are equal, otherwise 0 |
< | returns 1 if the prior number is less than the last number on the stack, otherwise 0 |
> | returns 1 if the prior number is greater than the last number on the stack, otherwise 0 |
~ | returns 1 if the last number is less 0.5, otherwise 0 |
h | treats last and prior numbers on stack as opposite and adjacent to return hypotenuse |
x | eXchange positions of last and prior numbers on the stack |
v | returns square root of last number on the stack |
q | returns cubed root of last number on the stack |
a | absolute value of last number on the stack |
s | sine of the radian value of the last number on stack |
c | cosine of the radian value of the last number on stack |
t | tangent of the radian value of the last number on stack |
S | arc-sine of the last number on the stack in radians |
C | arc-cosine of the last number on the stack in radians |
T | arc-sine of the last number on the stack in radians |
i | round down of last number on stack to nearest whole number / integer |
I | round up of last number on stack to nearest whole number / Integer |
l | log base 10 value of last number on stack |
n | natural log of the last number on the stack |
r | Convert the last value on the stack from radians to degrees |
R | Convert the last value on the stack from degrees to radians |
f | Convert the last value on the stack from Fahrenheit to Celcius |
F | Convert the last value on the stack from Celcius to Fahrenheit |
It is assumed if customising hardware, the builder will be aware of available pins and functions. loops and delays are the implementor's resposibility.
example script for headlamp switch:
# assuming pin 36 is a local DIN pin, use it as a light switch # it could have been defined as that in /auto.run before, eg # configpin 36 DIN # runbg /light.run :loop # only take action if in driving mode and switch position # does not match the setting of function 0 waitfor driving localpin.36 leadloco.function.0 = ~ & key 0 # wait time to allow switch to take place sleep 1 # loop back for next check goto :loop
Shared registers can be used to pass data from one script (or command prompt) to another. Here the shreg1 can be set at command prompt, but the I/O pin is is protected from the command prompt (or helper scripts) so a helper automation sets it to match.
configpin 4 PWM :loop waitfor shreg1 reg1 = ~ set reg1 shreg1 set localpin.4 reg1 goto :loop
Now at the command prompt it can be set:
run /setpwm.run set shreg1 255 set shreg1 127
Thank you for visiting camelthorn.cloud | Home |