Runtime Session (rt) - synthetos/TinyG GitHub Wiki
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.
Commands
| 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.
Heartbeat
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.
Quiescence token
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.
Feed-limit report
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.
Feed-limit push
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.
Supply gaps
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.
Gap frame
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](/synthetos/TinyG/wiki/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.