Robotic Piston
A robotic piston pushes, as a vanilla piston does, on any side a program names. It is how a computer moves blocks.
Facts
| | |
|—|—|
| Type | actuator |
| Address | Cabled: it is known by its id: name |
| Faces | Every side but the one with its cable port |
| Interrupt | None |
| Power | Nothing at rest; every push, extend or retract costs five ticks of a running CPU |
| Info | none beyond the common ones |
For the block, the plain and sticky kinds, and how to place it, see Robotic Piston.
How it moves
A push moves the blocks in front of it with vanilla piston mechanics, slide and all: the arm comes out, and goes back by itself after 6 ticks. A sticky piston pulls back what is in front of its arm when the arm retracts. An arm cannot be pushed by other pistons.
A piston has a front: the side opposite its cable port. A program can name a side by the world’s
names or relative to that front, so front is the side away from the cable. See
Faces. It can take a Bluetooth card in its card slot instead of a cable.
Every move costs power, whether or not it moved anything. Extending an arm that is already out is free.
In mcos
Its file is /dev/actuator0 (actuator1 for the next; its type is actuator, as
lsdev names it).
-
Read: it has no data, so a read says what it is, a line each:
type actuator id id:3f2a... path - label - methods extend push retract - Write: refused: opening it to write fails with
EACCES. - Poll: always ready for
IN(that text). It has no interrupt, so neverPRI. - ioctls: its methods below, by name, with the side as their argument. A move that is refused is
nil, its message (cannot push,not extended,no such face: ...) andEINVAL; a piston that has left the bus (its cable cut, say) isENODEV.setLabelsets its label, as on every device file.
local done, why = sys.ioctl(piston, "push", "front")
Methods
A side is a face name: north, south, east, west, up, down, or front, back, left,
right. A name that is no face fails with no such face; a relative name fails when the piston has
no front it knows, or left and right when its front is up or down.
push(side) → true
Pushes on that side. The arm goes out and comes back on its own. Fails with cannot push on the
cable’s side, when an arm is out there already, or when something in front will not move.
extend(side) → true
Pushes and leaves the arm out until retract. Gives true also when the arm is out already
(and then it stays out, free). Fails with cannot push otherwise as push does.
retract(side) → true
Takes the arm on that side back in; a sticky piston pulls what is in front of it. Fails with
not extended when there is no arm out there.
Events
None.
Examples
A hidden door: a lever on a redstone card opens it (retract) and closes it (extend):
-- A hidden door: a lever opens it, and it closes again when the lever is off.
-- lua piston-door.lua &
--
-- A sticky robotic piston holds the door's block: extend pushes it out into the doorway (closed),
-- retract pulls it back (open). The lever's signal comes in on a redstone card (/dev/redstone0, a
-- line a face: "face level powered"), and sys.poll waits for its change (PRI). The piston's file,
-- /dev/actuator0, has no data: its ioctls are its methods, extend and retract, by the side to move.
-- "front" is the side away from its cable, so the program works however the piston is turned.
local sys = require("sys")
local LEVER = "west" -- the face the lever's signal comes in on
local SIDE = "front" -- the piston's side that holds the door's block
local redstone = assert(io.open("/dev/redstone0"))
local piston = assert(sys.open("/dev/actuator0"))
local watch = {[sys.fileno(redstone)] = {PRI = true}}
local function lever()
redstone:seek("set", 0)
return (tonumber(redstone:read("a"):match(LEVER .. " (%d+)")) or 0) > 0
end
while true do
local open = lever()
local done, why
if open then
done, why = sys.ioctl(piston, "retract", SIDE)
if not done and why == "not extended" then
done = true -- (open already)
end
else
done, why = sys.ioctl(piston, "extend", SIDE) -- (closed already: true, and free)
end
if not done then
io.stderr:write("door: ", open and "open" or "close", ": ", tostring(why), "\n")
end
sys.poll(watch) -- block until the lever moves
end
A pusher, as a farm or a conveyor uses: a push every few seconds, waiting longer while it is
blocked:
-- Push whatever is in front of the piston every few seconds, as a farm's harvester or a block
-- conveyor does: lua piston-pusher.lua [side] &
--
-- push is a pulse: the arm goes out and comes back by itself. When nothing will move (the line
-- of blocks is too long, or something immovable is in the way) the push fails with "cannot push":
-- the program says so once, waits longer, and tries again, so it costs little while blocked.
-- Every push costs power, moved or not.
local sys = require("sys")
local PERIOD = 2 -- seconds between pushes
local BLOCKED = 10 -- seconds to wait after a push that could not move anything
local LOG = "/home/pusher.log"
local side = arg[1] or "front"
local piston = assert(sys.open("/dev/actuator0"))
local function log(text)
local file = assert(io.open(LOG, "a"))
file:write(os.date("%c"), " ", text, "\n")
file:close()
end
local blocked = false
while true do
local done, why = sys.ioctl(piston, "push", side)
if done then
if blocked then
log("moving again")
end
blocked = false
sys.sleep(PERIOD)
else
if not blocked then
log("blocked: " .. tostring(why))
end
blocked = true
sys.sleep(BLOCKED)
end
end
Every piston on the bus, pushed in turn, in bus order:
-- Fire every robotic piston on the bus in turn, a wave down a row of them:
-- lua piston-wave.lua [side] [rounds]
--
-- sys.devices() lists the pistons (type "actuator") in bus order, which you set at the computer;
-- each has a file in /dev (actuator0, actuator1...) whose ioctls are its methods. Each piston is
-- pushed on its front side (or the side given), one after another, DELAY seconds apart, for as
-- many rounds as asked. A piston that refuses ("cannot push") is reported and the wave goes on.
local sys = require("sys")
local DELAY = 0.25 -- seconds between one piston and the next
local side = arg[1] or "front"
local rounds = tonumber(arg[2]) or 1
local pistons = {}
for _, device in ipairs(sys.devices()) do
if device.type == "actuator" then
pistons[#pistons + 1] = {name = device.name, fd = assert(sys.open("/dev/" .. device.name))}
end
end
if #pistons == 0 then
io.stderr:write("piston-wave: no robotic piston on the bus\n")
os.exit(1)
end
for _ = 1, rounds do
for _, piston in ipairs(pistons) do
local done, why = sys.ioctl(piston.fd, "push", side)
if not done then
io.stderr:write("piston-wave: ", piston.name, ": ", tostring(why), "\n")
end
sys.sleep(DELAY)
end
end
for _, piston in ipairs(pistons) do
sys.close(piston.fd)
end