Create: Computing AgesBrass Docs
Devices

Hot Air Burner

The burner under a Create Aeronautics balloon: how much hot air it gives, how full its balloon is, and how a computer steers it.

Needs Create Aeronautics

The Hot Air Burner is a block of Create Aeronautics. Without that mod, there is no burner, and peripheral.find("hot_air_burner") returns nil.

The Hot Air Burner sits under a balloon and fills it with hot air: that is what lifts a Create Aeronautics ship. A computer connected to it reads how much hot air it makes and how full its balloon is, and above all it sets how much hot air the burner gives, which is how an autopilot climbs, holds its height or keeps a ship level.

Its peripheral type is hot_air_burner. Against the computer it is called by its side ("top"...); on a Data Cable (Minicomputer and newer) it gets a name such as hot_air_burner@4,70,-2. A ship usually has several: peripheral.list("hot_air_burner") gives all their names.

Brass
local burner = peripheral.find("hot_air_burner")
print(burner.output() .. " m3 of hot air, signal " .. burner.signal())

Every method reads the block again when it is called. When the burner is gone, the call stops the program with burner removed (for amount and set_amount) or sensor removed (for the others); when its chunk is not loaded, with peripheral is not loaded. Like the sensors, it has a read() that returns all its readings in one table (see sensor.read()): output, signal, amount, filled and capacity.

Methods
burner.output()The hot air the burner gives now, in m³: its amount times its redstone signal, divided by 15. 0 when the flame is out.
burner.signal()The strongest redstone signal the burner receives, from 0 (flame out) to 15 (full flame).
burner.amount()The amount set on the burner's value box ("Max Hot Air Output"), in m³: how much hot air it asks for at full flame. Between 5 and the server maximum (500 by default, server config hot_air_burner_max). A new burner starts at the maximum.
burner.set_amount(m3)Sets the burner's amount, like turning its value box, and returns the value it kept.
burner.filled()How much hot air is in the balloon above the burner right now, in m³. It creeps towards the target (the sum of the outputs of its burners, at most the capacity), so it lags behind every change of amount.
burner.capacity()The volume of the balloon above the burner, in m³: the number of air blocks it encloses. nil without a balloon, as for filled.

How a balloon flies

A program steers a balloon well only if it knows how the balloon behaves. Five things to know:

1. The balloon is the air under an airtight roof. The burner looks straight up (up to 80 blocks by default, the server config hot_air_burner_max_range) for the first airtight block, such as a Hot Air Envelope. The balloon is every layer of air under that roof that is closed in on its four sides. An upside-down cup of envelope, open at the bottom, works: the burner goes under the opening. Its capacity is the number of air blocks inside, one block of air being 1 m³: a room 3 blocks wide, 3 long and 2 high holds 18 m³.

2. The amount is a target, not a flow. The value box on the burner (5 to 500 m³ by default) is how much hot air the burner wants in its balloon, not how much it pushes each second. The burner asks for amount × signal / 15: all of it at a redstone signal of 15, a third at 5. Several burners under the same balloon add up their requests.

3. Never more than the capacity. The balloon heads for the sum of the requests, but stops at its capacity: anything above is wasted. A burner set to 500 under a balloon of 18 fills it no better than one set to 18.

4. The gas is slow. The hot air does not jump to its target: it creeps towards it. Far from the target it takes about 9 seconds to close two thirds of the gap, near it a second or two. So a ship answers several seconds after a change. A program that pushes hard every time the height is wrong will overshoot, again and again: it has to be gentle and watch the climb speed (see Airship altitude hold).

5. Lift weakens with height. The hot air in the balloon lifts the ship, but less and less as the air thins (see altitude_sensor.pressure()). So a ship settles at the height where its lift equals its weight: give it more hot air and it climbs to a new balance, less and it sinks to one. Every ship also has a ceiling, where even a full balloon cannot carry it higher.

Lighting the flame. The flame follows redstone, and only redstone: the burner takes the strongest signal around it, again each time a neighbouring block changes. No method lights it, so the computer gives it redstone:

  • a burner against the computer: rs.set("top", 15) (the side it is on);
  • a burner further away: a Redstone Link next to it, driven with link.set (see link and Redstone Link);
  • a flame that always burns: a lever or a Block of Redstone against the burner.

Why the computer sets the amount. Redstone has only 15 steps. The amount goes from 5 to 500 in steps of 1 (by hand, the value box moves in steps of 5), and the computer can change it on every burner at any time. The usual design: keep every flame at 15, and steer with the amount alone. Then the burner asks for exactly its amount.

The flame

#

burner.output()

→ number

The hot air the burner gives now, in m³: its amount times its redstone signal, divided by 15. 0 when the flame is out.

Returns
number
the hot air the burner asks for now, in m³

This is the burner's share of the balloon's target (see "How a balloon flies" above), not a flow per second. Engineer's Goggles show the same number as "Hot Air output".

Brass
local burner = peripheral.find("hot_air_burner")
print(burner.amount(), burner.signal(), burner.output())

With an amount of 120 and a signal of 10, it prints 120, 10 and 80.

See also hot_air_burner.amount() hot_air_burner.signal()

#

burner.signal()

→ number

The strongest redstone signal the burner receives, from 0 (flame out) to 15 (full flame).

Returns
number
the redstone power that lights the burner, from 0 to 15

A burner without redstone gives nothing, whatever its amount: check it first when a ship does not rise.

Brass
for _, name in ipairs(peripheral.list("hot_air_burner")) do
  local burner = peripheral.wrap(name)
  if burner.signal() == 0 then
    print(name .. " is not lit: give it redstone")
  end
end

A burner touching the computer is lit by the computer itself:

Brass
rs.set("top", 15)   -- the burner sits on top of the computer

The burner reads its new signal when the computer's output changes, at the end of the program's tick.

See also hot_air_burner.output()

The amount

#

burner.amount()

→ number

The amount set on the burner's value box ("Max Hot Air Output"), in m³: how much hot air it asks for at full flame. Between 5 and the server maximum (500 by default, server config hot_air_burner_max). A new burner starts at the maximum.

Returns
number
the amount set on the burner, in m³
Brass
local burner = peripheral.find("hot_air_burner")
print("set to " .. burner.amount() .. " m3")

See also hot_air_burner.set_amount()

#

burner.set_amount(m3)

→ number

Sets the burner's amount, like turning its value box, and returns the value it kept.

Parameters
m3 number
the new amount, a whole number of m³
Returns
number
the amount kept, between 5 and the server maximum

The value is kept between 5 and the server maximum (500 by default), and any whole number in between works, finer than the steps of 5 of the value box. The number must be whole: burner.set_amount(12.5) stops the program with bad argument #1 to 'set_amount' (number has no integer representation), so round your results with math.floor.

Brass
local burner = peripheral.find("hot_air_burner")
print(burner.set_amount(42))
print(burner.set_amount(2))
print(burner.set_amount(100000))

On a server with the default settings, this prints 42, then 5 (the minimum), then 500 (the maximum). That last trick finds the server's maximum without knowing it: ask for far too much, and read what was kept.

There is no point asking for more than the balloon holds: the useful top is the capacity of the balloon (divided by signal / 15 when the flame is not full). This keeps a balloon exactly full, with nothing wasted:

Brass
local burner = peripheral.find("hot_air_burner")
local cap = burner.capacity()
if cap then
  burner.set_amount(cap)
end

See also hot_air_burner.amount() hot_air_burner.capacity()

The balloon

#

burner.filled()

→ number|nil

How much hot air is in the balloon above the burner right now, in m³. It creeps towards the target (the sum of the outputs of its burners, at most the capacity), so it lags behind every change of amount.

Returns
number|nil
the hot air in the burner's balloon now, in m³; nil without a balloon

nil when the burner has no balloon: nothing airtight above it, or a burner that has never been lit (it only looks for its balloon while its flame burns). Several burners under the same balloon all read the same number.

Brass
local burner = peripheral.find("hot_air_burner")
local hot = burner.filled()
if hot == nil then
  print("no balloon above this burner")
else
  print(string.format("%.1f m3 of hot air", hot))
end

See also hot_air_burner.capacity()

#

burner.capacity()

→ number|nil

The volume of the balloon above the burner, in m³: the number of air blocks it encloses. nil without a balloon, as for filled.

Returns
number|nil
the volume of the burner's balloon, in m³; nil without a balloon

With filled, it tells how full the balloon is:

Brass
local burner = peripheral.find("hot_air_burner")
local cap = burner.capacity()
if cap then
  print(math.floor(burner.filled() / cap * 100) .. " % full")
end

A balloon gauge for the bridge of the ship: the envelope fills from the top, as hot air does. The drawing takes the table read() returns; here it is tried with a reading typed by hand, a balloon of 18 m³ with 12.4 m³ inside:

Brass
local function gauge(b)
  gfx.clear("black")
  term.clear()
  local cx, cy, r = 70, 70, 50
  local share = b.filled / b.capacity
  gfx.circle(cx, cy, r, "orange", true)
  -- hide the part not yet filled: hot air gathers at the top
  local empty = math.floor(2 * r * (1 - share))
  if empty > 0 then
    gfx.rect(cx - r, cy + r - empty + 1, 2 * r + 1, empty, "black", true)
  end
  gfx.circle(cx, cy, r, "light_gray", 2)
  gfx.line(cx - 12, cy + r, cx - 8, cy + r + 22, "brown")
  gfx.line(cx + 12, cy + r, cx + 8, cy + r + 22, "brown")
  gfx.rect(cx - 10, cy + r + 22, 21, 12, "brown", true)
  if b.signal > 0 then
    gfx.triangle(cx - 4, cy + r + 21, cx + 4, cy + r + 21, cx, cy + r + 12, "yellow", true)
  end
  term.set_cursor(26, 3)
  term.write("BALLOON")
  term.set_cursor(26, 5)
  term.write(string.format("%.1f / %d m3", b.filled, b.capacity))
  term.set_cursor(26, 6)
  term.write(math.floor(share * 100) .. " % full")
  term.set_cursor(26, 9)
  term.write("BURNER")
  term.set_cursor(26, 11)
  term.write("amount " .. b.amount .. ", signal " .. b.signal)
  term.set_cursor(26, 12)
  term.write("output " .. b.output .. " m3")
end

gauge({output = 18, signal = 15, amount = 18, filled = 12.4, capacity = 18})
Screen
Screen

In the game, call gauge(burner.read()) in a loop, after checking that capacity is not nil.

See also hot_air_burner.filled() hot_air_burner.set_amount()

A throttle that wastes nothing

A complete little program: an Analog Lever on the left of the computer becomes the throttle of a balloon. The burner sits on top of the computer, which lights it; the lever's 16 positions spread between the minimum amount and a full balloon, so no notch is wasted above the capacity. The program sleeps until the lever moves.

Brass
local burner = peripheral.wrap("top")
rs.set("top", 15)   -- light the flame
sleep(1)            -- time for the burner to find its balloon

while true do
  local throttle = rs.get("left")   -- 0 to 15
  local top = burner.capacity() or 500
  local amount = burner.set_amount(math.floor(5 + (top - 5) * throttle / 15))
  print("throttle " .. throttle .. ": " .. amount .. " m3")
  os.pull_event("redstone")
end

For a whole ship, the same idea drives every burner in a loop over peripheral.list("hot_air_burner"), and a sensor replaces the lever: Airship altitude hold holds a height and keeps the ship level, step by step, and the built-in program balloon is the complete autopilot.