Repository navigation
Runtime Session (rt)
The runtime session family rt holds state that belongs to the current host session only: an idle heartbeat, a quiescence token, a report of why the move running now is slower than its F, and pushed feed-limit summaries. Everything in it lives in RAM, is off or empty after every reset (including ^X), and is never written to EEPROM. It is JSON only: there is no $rt... text form. Firmware without it answers {rt:null} with status 100 (unrecognized name), which is how a host checks for it.
These are tools for host software (senders, UIs) and for the people writing it:
-
Heartbeat (
hb): is the board still there? While a job runs, replies and status reports show the board is alive. When it is idle the link goes silent, and a host cannot tell a quiet board from an unplugged cable or a reset without polling. Polling has its own cost: every query is a line the board must parse and answer, and a query that crosses a settings write or a job start can collide with it. Withhbset, the board itself says "still here, idle, this is my state" every N seconds, and only while nothing else is happening. -
Quiescence token (
qt): did anything run while I was away? After a host crash, a browser tab reload or a cable glitch, a reconnecting host does not know whether the board executed something in the meantime. It installs a random token while the machine is idle; any G-code admitted, any setting changed, a job kill or a flush clears it. Reading the same token back proves nothing happened, so the host can safely resume its view of the machine instead of guessing. -
Feed-limit report and push (
flk..flf,fi,fs,fl): why is this job slower than its feeds? A job with F2000 that runs at 400 mm/min is usually not a fault: corners, short CAM segments, an axis speed limit or exact-stop mode keep the planner below F. The board knows which, because it planned every move. These leaves and frames hand that knowledge to the host: the current move's limit, and over a job the cutting time lost to each cause. A UI can then tell the operator "slow because of short segments" instead of "slow". -
Job flag and supply gaps (
job,fg): is the machine or the host the bottleneck? When the machine runs out of work mid-job, the cause can be the sender not keeping up (USB, a busy PC, a slow G-code stream) rather than the motion planner. With the job flag set, the board counts those starved moments and says whether input was waiting on the board or never arrived, so a host can show "the sender did not keep up" rather than blaming the machine.
All of it is optional and off after a reset; a host that never sends rt gets exactly the classic behaviour.
TinyG runs on an 8-bit ATxmega192 with no floating-point unit. The motion planner has always used software floating point, and step generation runs from interrupts; that is where the processor's time goes. The rt family is built to add as little as possible to that:
-
Parsing:
rtlines go through the same JSON parser as every other command, and a host sends them rarely (at connect, job start and job end), not per G-code line. - Heartbeat: sent only while the machine is idle and the link is quiet, so it never competes with a job.
- Feed-limit accounting, when off (the default): one flag test per segment.
- When on: per segment (about 5 ms of motion) one comparison and one multiplication, the rest integer additions; each move is classified once when it starts. Totals are folded every ~250 ms with integer arithmetic. Summaries are formatted only when the output buffer is empty, using integer formatting only (the distance is printed as whole mm and three digits). Measured cost: about 2% of throughput on very dense G-code that keeps the planner fully busy, nothing noticeable otherwise.
- Memory: RAM only, never written to EEPROM.
| Command | Effect |
|---|---|
{rt:null} |
Read the family: {"r":{"rt":{"hb":0,"qt":null,"up":1234,"flk":0,"flm":0,"flp":null,"flf":null}},...}. hb heartbeat interval in seconds (0 = off), qt the quiescence token or null, up seconds since the board started, flk..flf the feed-limit report. |
{rt:{hb:N}} |
Heartbeat every N seconds while idle, N = 1 to 60. {rt:{hb:0}} turns it off. A whole number above 60 is refused with status 110, a fraction or a string with 116; true counts as 1. |
{rt:{qt:"<32 hex digits>"}} |
Install a quiescence token (below). |
{rt:{qt:null}} |
Read the token alone. |
{rt:{flk:null,flm:null,flp:null,flf:null}} |
Read the feed-limit report alone (any subset of the four). |
{rt:{fi:N}} |
Push feed-limit lines with a summary every N seconds while a job runs, N = 1 to 60 (larger is clamped; the reply shows the value applied). {rt:{fi:0}} stops. |
{rt:{fs:null}} |
Push a summary now; the reply value is the q it will carry, null if accounting was never turned on. |
{rt:{job:1}} / {rt:{job:0}}
|
The host's job flag: 1 while it streams a job and has more motion to send, 0 after its last motion line and before its own deliberate stops. {rt:{job:null}} reads it. |
{rt:{fg:1}} / {rt:{fg:0}}
|
Opt in to (or out of) the gap frame that follows every summary. {rt:{fg:null}} reads it. |
Rules, all checked before anything changes:
-
rtstands alone on its line:{rt:{hb:1},sv:1}is refused with status 106. Atidmay accompany it. - A token write stands alone too:
{rt:{hb:1,qt:"..."}}is refused (106). - No key may appear twice, inside
rtor at the top level (106). - A refused leaf (out-of-range
hb, malformed token, unknown key) is the reply's status and changes nothing. - The flat forms (
{rthb:1}), text mode ($rthb=1), atxtcontainer and status report lists cannot reachrt(status 100). - Writes are refused while the machine is in ALARM; reads still work.
With hb set, the board sends one line every hb seconds while it is idle:
{"hb":{"stat":3,"hold":0,"up":1234,"qt":null}}
-
statandholdare the machine and hold states;upis seconds since start (it wraps with the tick counter after about 48 days);qtis the token, quoted, or null. - A heartbeat is not a response: it carries no
rand no footer, so it never counts as an acknowledgement in line mode or character counting. - Idle means no motion, cycle, hold, dwell, queued or prepared move, flush or job kill pending, the board in JSON mode, and the USB link quiet both ways: no byte received or sent since the last check (realtime characters included), no unread or partial input, nothing waiting to go out. Any activity restarts the interval, so there are no beats during a job.
- A beat is sent only if the whole line can go out at once: nothing else queued for output, and output not paused by XOFF or by CTS (RTS/CTS flow control). A skipped beat is not made up.
- Silence does not prove that the board reset or disconnected; it means the link is stale. Count any reply, status report or heartbeat as a sign of life.
A host installs a random token of its choosing (32 hex digits) and reads it back after a reconnect:
- Same token: no tracked state-changing input reached the board since it was installed.
- No token: the board reset, or something changed state.
- A different token: another host installed one.
The token is accepted only when nothing is moving, held, flushing, queued or prepared, and no older data line is waiting (status 106 otherwise). It is cleared at reset and before any of these take effect:
- any G-code line (raw,
{gc:...}or intxt), including G10, G92 and G28.x; - any setting or action write, in JSON or text mode, including
home, jog and$defa; - GETs that act:
{clear:null},{clr:null},{me:null},{md:null},{alarm:null},{panic:null},{shutd:null}, also when they are part of a status report or group; - an accepted queue flush (
%), a job kill (^D), and any motion starting or resuming.
Reads, ENQ, ! and ~ leave it. It is not authentication, and an equal token does not prove that nothing changed physically (someone could move an axis by hand). A host that finds it missing or different should treat the machine position as unknown.
Four read-only leaves describe the move that is stepping now (not the one being planned or prepared), and why its planned speed is below the F it asked for:
| Leaf | Meaning |
|---|---|
flk |
Kind: 0 no move running (idle, held, dwelling, running a queued command), 1 feed move (G1, G2, G3), 2 rapid (G0). |
flm |
Constraint tags of the move's plan, a bit mask (below). 0 when no move is running. |
flp |
Planned cruise speed as a percent of F, rounded. Only for G94 feed moves of the linear axes (X, Y, Z); null for rapids, G93 inverse time, moves with a rotary axis, and when no move is running. It can read above 100 when the plan reaches F within rounding. |
flf |
The move's F in mm/min. Null for rapids, G93 and when no move is running. |
Tag bits in flm:
| Bit | Name | Meaning |
|---|---|---|
| 1 | axis cap | An axis's feedrate maximum ($xfr etc.) makes the move slower than its F. |
| 2 | corner | The move's entry speed is limited by the corner with the move before it. |
| 4 | exact stop | G61 exact stop: the move starts and ends at rest. |
| 8 | short | The move is too short to reach its cruise speed between its entry and exit. |
| 16 | single segment | The move is so short it runs as one segment. |
| 32 | command | A queued command next to the move (S, M3-M5, M7-M9 and the like) makes it stop at that end. |
- All four leaves of one reply describe the same move: the report is taken once per line.
- A tag means the constraint took part in planning the move, not that it limits every instant of it. An absent tag is not proof that the constraint was absent: in particular a move queued just as the move before it finished may get no corner tag.
-
flpcompares the planned cruise with F; it is not the instantaneous speed. Usevelin a status report for the speed now. - Writing a leaf is refused.
With fi set the board pushes two kinds of line on its own. Like the heartbeat they are not responses: they carry no r and no footer and never count as an acknowledgement.
Summary every fi seconds while a cycle runs, and once more after the cycle has finished and every move has played out:
{"fs":{"v":2,"e":1,"q":3,"c":[1200,830,0,410,25,960,0],"ct":5230,"cd":80.000}}
| Field | Meaning |
|---|---|
v |
Format version (2). |
e |
Window: counts up each time accounting is turned on; 0 after a reset. |
q |
Summary number in this window, from 1. A gap means a summary could not be sent; nothing is lost, because the totals are cumulative. |
c |
Milliseconds below F per cause, in the order axis cap, corner, exact stop, short, single segment, other, command. "Other" is time below F with no tag: normal start and stop ramps, the end of the queued path, slowing for a later corner. "Command" is time below F of moves that stop at a queued command. A moment can count under several causes, so the seven can add up to more than ct. |
ct |
Cutting time in milliseconds: the scheduled time of every finished G94 feed segment of the linear axes (X, Y, Z). |
cd |
Cutting distance in mm (always mm, also under G20), commanded along the path. An estimate, not measured travel. |
x |
Present (1) only when the totals of this window are known to be short; see below. |
All values are totals since fi was turned on (the window). A host gets per-interval figures by subtracting two summaries. Only finished segments count: rapids (G0), G93 inverse time moves and moves with a rotary axis are not cutting; the deceleration into a feedhold is not counted; a segment cut short by a reset, shutdown or kill is not counted.
Cause change when the executing move's kind or tags change, at most 4 a second, carrying the latest state:
{"fl":{"k":1,"m":2,"p":61,"n":20}}
{"fl":{"k":0,"m":0,"p":null,"n":null}}
k, m and p are as flk, flm and flp above; m includes the short and single-segment tags. n is the line number of the move executing (null when no move runs). While a supply gap is open the frame adds "g":1 (input is on the board but not yet run) or "g":2 (no complete input is known: the host did not send in time), and n is the last line run.
Rules:
-
{rt:{fi:N}}turning accounting on opens a new window with zeroed totals and is accepted only when the machine is idle with nothing queued or waiting (status 106 otherwise). Changing N while on keeps the totals.fi:0stops counting and keeps the totals for{rt:{fs:null}}; a summary that had not gone out yet is dropped. - Setting
fidoes not clear the quiescence token. -
x:1: if the board's main loop is held up long enough for its internal counters to wrap (about 24 hours of cutting without a chance to fold them, e.g. output stalled by flow control during a very long move), every later summary of that window carries"x":1and its totals are short. Treat them as invalid until a new window. - The millisecond totals wrap after 2^32 ms (49.7 days of cutting);
cdafter 2^32 mm. - The counting costs nothing measurable while off. While on, it uses some processor time in the step generation, about 2% of throughput on very dense G-code that keeps the planner fully busy, and nothing noticeable otherwise.
With accounting on and the host's job flag set, the board counts the time it ran out of work: a stop after motion began in this job where nothing is stepping, dwelling, prepared or runnable, outside a feedhold, M0/M1, alarm, flush, job kill or program end. Two classes:
-
Input pending (
g1): input has reached the board but has not run yet (unread serial data, a buffered line, an arc still being generated). -
No input (
g2): no complete line is known; the host did not send in time.
The host sets {rt:{job:1}} when it starts streaming and {rt:{job:0}} after its last motion line, so the normal stop at the end of a job, and any stop the host makes on purpose, are not counted. A reset, %, a job kill and program end clear the flag; a feedhold does not.
With {rt:{fg:1}}, every summary is followed by a gap frame with the same e and q:
{"fg":{"e":1,"q":3,"g":[120,840,3],"b":[[2,410],[0,0],[1,90]]}}
| Field | Meaning |
|---|---|
g |
Supply-gap time in ms with input pending, with no input, and the number of gaps (an open gap's running time included). |
b |
Per command class (spindle and S, coolant, other): the number of motion breaks at command boundaries and the ms below F of the moves those commands affect. |
x |
Present (1) only if one of these counters wrapped. |
The gap frame is opt-in because a host that does not know it would see an unexpected line. It is off after a reset; opening a new fi window does not turn it off.
Hosts should ignore unknown keys in fl, fs and fg: new keys may be added; existing keys keep their meaning, and c keeps its order.
Getting Started Pages
- Home
- What is TinyG?
- Getting Started
- Connecting TinyG
- Configuring TinyG
- Sending Gcode Files
- Flashing TinyG
- Chilipeppr
Reference Pages
- TinyG Help Page
- TinyG Tuning
- TinyG Command Line
- TinyG JSON
- How TinyG Keeps Real Time
- Jogging from a Host
- Gcode Support
- Homing and Limits
- Inch and MM Units
- Alarms and Exceptions
- Coordinate Systems
- Status Codes
- Status Reports
- Runtime Session (rt)
- Power Management
- Feedhold and Resume
- Licensing
- TinyG v8 Data Sheet
Discussion Topics
- Test-Drive-TinyG
- Jerk Controlled Motion
- Gcode Parsing
- Shapeoko Setup
- OX CNC TinyG Guide
- Creating Gcode Files
- Milling With Noisy Spindles
- Stepper Motors and Power Supplies
- Text Wrappers and Transaction IDs
- Using External Drivers
- TinyG Projects
Chilipeppr
- Chilipeppr
- Chilipeppr Advanced Usage
- Chilipeppr Archive and Restore Parameters
- ChiliPeppr PCB Auto Level
- Automatic Z Homing When Milling PCBs
Troubleshooting
Developer Pages