This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
Onionnion's profile picture

os.pullEvent(): What is it and how is it useful?

Started by Onionnion, 30 April 2012 - 01:15 AM
Onionnion #1
Posted 30 April 2012 - 03:15 AM
os.pullEvent(): What is it and how is it useful?

For me, os.pullEvent() has proven to being one of the most useful tools available for ComputerCraft, but it can be quite confusing for many at first and I won't deny it for me as well (took me a couple days before I figured out how to use it properly..probably longer).

So what is os.pullEvent()?
os.pullEvent() is a function that's used to detect events of any type, which can be pressing a button on the keyboard or an incoming rednet message. For this brief guide I will just explain the basics of how it works and how to use it because of how easy it is to plug it in for other uses.
Hint:
SpoilerWhat an event is exactly is pretty much exactly what the word is: an event. This event is any type of input that the computer can use. Look below for all the different event types.

A quick demonstration
Open a computer in Minecraft and enter the Lua command prompt ('lua' is the command as we should all know by now). Enter this short program:

while true do print(os.pullEvent()) end
After entering this feel free to press some buttons on your keyboard and you can see that it prints things like 'key30' or 'charj'.

What is it doing?
Basically what os.pullEvent() does is return values depending on certain inputs, or events, that happen, such as a keyboard button press or a redstone input. It returns these values based on this syntax:

event, param1, param2 = os.pullEvent() -- param = parameter
When os.pullEvent() is called, it waits, or "yields" for an input (depending on what's inside its parameters which in this case is nothing so it accepts all events; will cover this later) and once an input is recieved it applies it in like shown above.
Here's how the function works:

  1. When called, it waits for an input (depending on parameter arguments)
  2. Once an input is detected it sets a string to the first variable, which in this case is 'event', as the name of the event that occurred.
  3. It then sets other parameters to other variables depending on the event type. These parameters and the number used is dependent on the event type.

Examples of usage:
Let's say you were developing a program that included key-pressing to choose options (press a to do this, press b to do that, etc.). How such a thing would be written is actually quite simple:

function clear(test) -- function to reset screen
	term.clear()
	term.setCursorPos(1,1)
	if test == nil then -- easy way to decide whether to display text or not
		print("Press 1 to say Hi, 2 to say Bye or 3 to exit.")
	end
end

clear()

while true do
	event, param1 = os.pullEvent()
	if event == "char" and param1 == "1" then -- if the event was a character input and the character pressed was 1
		print("Hi")  -- print "Hi"
		sleep(2)	 -- sleep for 2 seconds
		clear()	  -- call the clear function
	elseif event == "char" and param1 == "2" then -- if the event was a character input and the character pressed was 2
		print("Bye") -- print "Bye"
		sleep(2)	 -- sleep for 2 seconds
		clear()	  -- call the clear function
	elseif event == "char" and param1 == "3" then
		break -- breaks loop
	end
end -- will repeat until loop is broken with 'break' (3 being pressed)

clear(1) -- reset screen but without text

Another good example would be for detecting rednet messages:

side = "back"	 -- it's always good practice to set a variable to the side being used
rednet.open(side) -- opens that side's rednet port

function clear()  -- screen-clearing function
	term.clear()
	term.setCursorPos(1,1)
end

clear()

while true do
	event, param1, param2 = os.pullEvent("rednet_message") -- with rednet messages, the second variable is the ID of
														   --   the computer from which it was sent (integer) and
														   --   the third is the message itself (string)
														   -- parameter argument "rednet_message" limits the event
														   --   accepted to just "rednet_message"
	if param2 == "e" then -- if the message received is the string "e",
		break			 --   break the loop and exit
	end
	print(param1..": "..param2) -- if the loop isn't broke, it will print from which the ID came from and
								--   the message itself
end

clear() -- clear the screen before exiting
Hint:
SpoilerPutting an event type in the parameters as an argument is for limiting the function to accepting that event only. So far only one argument is accepted. If more than one is placed (each separated by a comma), it will only accept the first argument listed.

Something to try:
Take the code of the first example and make it so it doesn't need 'event == "char"' in the boolean (true or false; if) operations.

All event types (from the events help file)
  • "char" when text is typed on the keyboard. Argument is the letter typed.
  • "key" when a key is pressed on the keyboard. Argument is the numerical keycode.
  • "timer" when a timeout started by os.startTimer() completes. Argument is the value returned by startTimer().
  • "alarm" when a time passed to os.setAlarm() is reached. Argument is the value returned by setAlarm().
  • "redstone" when the state of any of the redstone inputs change.
  • "disk" or "disk_eject" when a disk is entered or removed from an adjacent disk drive. Argument is the side.
  • "rednet_message" when a message is received from the rednet API.
Hint:
SpoilerFor a quick reference of all events, enter 'help events' in a computer.

FAQ
(Idea for something to put here? Please share!)
How do I use Redstone events?? The Redstone event doesn't return any parameters!

if event == "redstone" and rs.getInput(side) == true then
	print("blah")
end


Understand yet? Feel free to ask any questions and I'll try my best to help. Any suggestions? Feel free to give! I'll also soon add more advanced ways to use this!
Rwkeith #2
Posted 03 May 2012 - 06:11 AM
Great guide. And this is one of the most useful commands in Computercraft =)
EmTeaKay #3
Posted 03 May 2012 - 11:43 PM
I thought you were asking what it was.
DrEckenbecker #4
Posted 04 May 2012 - 03:46 AM
Fantastic guide. helped me a lot. The wiki needs some work on os.pullEvent()… I don't want to copy your guide, but it does seem to be wikiworthy :)/>/>
Onionnion #5
Posted 04 May 2012 - 02:14 PM
Fantastic guide. helped me a lot. The wiki needs some work on os.pullEvent()… I don't want to copy your guide, but it does seem to be wikiworthy :)/>/>
Currently we are working on the wiki and I am working on the OS API page and its functions: http://www.computercraft.info/forums2/index.php?/topic/1542-wiki-calling-for-help/page__view__findpost__p__11434

Thanks!
Smidge #6
Posted 08 May 2012 - 06:19 AM
Wow. My mind is blown with ideas now. Thanks.
Also, what is the difference between "char" and "key" events?
BigSHinyToys #7
Posted 08 May 2012 - 06:58 AM
Wow. My mind is blown with ideas now. Thanks.
Also, what is the difference between "char" and "key" events?

"char" events return a string example "q" "y" "n"
"key" events return a number example 200 208 203 205
the numbers represent key in the case of the above.
200 is up key
208 is down key
203 is left key
and 205 is right key

In piratical use



local numb = 1
term.clear()
term.setCursorPos(numb,1)
print("Press q or Q to Quit")
term.setCursorPos(numb,2)
print("Press left key or right key to move text")
while true do
local event , param1 = os.pullEvent()
if event == "key" then
  if param1 == 203 then -- left key
  numb = numb-1
  end
  if param1 == 205 then -- right key
  numb = numb+1
  end
end
term.clear()
term.setCursorPos(numb,1)
print("Press q or Q to Quit")
term.setCursorPos(numb,2)
print("Press left key or right key to move text")
term.setCursorPos(numb,3)
if event == "char" and param1 == "q" or param1 == "Q" then
  print("Ended")
  break
else
  print("Not Ended")
end
end
Onionnion #8
Posted 08 May 2012 - 02:34 PM
Wow. My mind is blown with ideas now. Thanks.
Also, what is the difference between "char" and "key" events?
I'll ad that into the guide.
Thanks for answering his question, BigSHinyToys.
DrFreenote #9
Posted 16 June 2012 - 04:09 AM
Hey, just a quick question for anyone who wants to answer; Regarding the key values, is there a comprehensible list of values and their associated key names? I've found one or two in various spots (it was mentioned above that "200" relates to the up arrow key, "208" relates to the down arrow key, et cetera), but I haven't actually found a list that clearly defines all the keys that will function.

EDIT: On that note, for those who are looking for a specific key value, open up the LUA console and type in the os.pullEvent("key") function. The value of the next key you press will show. (Though you all probably already know this and I'm just being silly.)
MysticT #10
Posted 16 June 2012 - 07:28 PM
Hey, just a quick question for anyone who wants to answer; Regarding the key values, is there a comprehensible list of values and their associated key names? I've found one or two in various spots (it was mentioned above that "200" relates to the up arrow key, "208" relates to the down arrow key, et cetera), but I haven't actually found a list that clearly defines all the keys that will function.

EDIT: On that note, for those who are looking for a specific key value, open up the LUA console and type in the os.pullEvent("key") function. The value of the next key you press will show. (Though you all probably already know this and I'm just being silly.)
Here's the list of key codes used by minecraft (and by extention, computercraft): wiki
DrFreenote #11
Posted 16 June 2012 - 11:07 PM
Thank you very much, MysticT! This is exactly what I needed! =3

EDIT: I have another question out of curiosity, what would be the best way to detect simultaneous keystrokes? (Shift+Enter, Ctrl+Up, Ctrl+Shift+Tab, you get the idea.) Is it even possible? It's not a big thing, I can work around not being able to, though it would be rather helpful to know.

I figure this would relate to os.pullEvent(), though I'm not exactly sure how. I don't know of any way to detect when a key is released, though I could just be blindly overlooking something obvious.
MysticT #12
Posted 17 June 2012 - 12:32 AM
Thank you very much, MysticT! This is exactly what I needed! =3

EDIT: I have another question out of curiosity, what would be the best way to detect simultaneous keystrokes? (Shift+Enter, Ctrl+Up, Ctrl+Shift+Tab, you get the idea.) Is it even possible? It's not a big thing, I can work around not being able to, though it would be rather helpful to know.

I figure this would relate to os.pullEvent(), though I'm not exactly sure how. I don't know of any way to detect when a key is released, though I could just be blindly overlooking something obvious.
Currently there's no reliable way to do it. There should be a "key release" event or something, so we can keep track of the pressed keys, and detect combinations and other things.
DrFreenote #13
Posted 17 June 2012 - 04:25 AM
Alright, good to know. Thanks again, MysticT!
5olstitium #14
Posted 17 June 2012 - 05:48 AM
Can you explain os.pullEventRaw() vs os.pullEvent() please?
DrFreenote #15
Posted 17 June 2012 - 07:26 AM
Can you explain os.pullEventRaw() vs os.pullEvent() please?
People can correct me if I'm wrong, but it seems the only notable difference is that os.pullEventRaw() will detect and allow intervention of termination, reboot, or shutdown events caused by the built in keybinds (Ctrl+T, Ctrl+R, and Ctrl+S respectively), whereas os.pullEvent() will not.
Pinkishu #16
Posted 17 June 2012 - 11:30 AM
Can you explain os.pullEventRaw() vs os.pullEvent() please?
People can correct me if I'm wrong, but it seems the only notable difference is that os.pullEventRaw() will detect and allow intervention of termination, reboot, or shutdown events caused by the built in keybinds (Ctrl+T, Ctrl+R, and Ctrl+S respectively), whereas os.pullEvent() will not.

I think thats wrong :(/>/>

pullEventRaw and pullEvent are both defined in bios.lua btw



function os.pullEventRaw( _sFilter )
  return coroutine.yield( _sFilter )
end

function os.pullEvent( _sFilter )
  local event, p1, p2, p3, p4, p5 = os.pullEventRaw( _sFilter )
  if event == "terminate" then
    print( "Terminated" )
    error()
  end
  return event, p1, p2, p3, p4, p5
end

If you press Ctrl+R or Ctrl+S the mod itself handles the shutdown/reboot
If you press Ctrl+T a "terminate" is added to the event queue
Now If you call os.pullEvent as you see it will just call os.pullEventRaw and temrinate the program if it finds that event

So if you do os.pullEvent = os.pullEventRaw, what many people do to prevent Ctrl+T
you just reroute any os.pullEvent call to the direct os.pullEventRaw which does not check for the terminate event
DrFreenote #17
Posted 17 June 2012 - 10:27 PM
Thank you for clearing that up, Pinkishu! =3

I do believe you saved me a million and one future headaches; I could have seen myself spending hours trying to figure out why my programs wouldn't prevent reboots or shutdowns.

On that note, referring to my earlier question regarding multiple keypresses, I spotted a predictable pattern in how keypresses are called. When a key is pressed, if that key contains a printable character, it will send two seperate events; The 'key' event, which outputs the key number, and the 'char' event, which outputs the character the key represents. The 'char' event can be used on it's own to detect Shift+[key] presses, as the character in question will be capitalized, though this is not reliable as CapsLock will then act as if Shift is perpetually being held (Quite possibly leading to false triggers). Unless there is a way to detect if CapsLock (or even NumLock) is activated, I wouldn't trust this method.

However, I've noticed a certain behavior when it comes to Ctrl+[key], assuming the key is normally associated with a 'char' event. The event in question simply refuses to trigger. Very consistantly, it seems. I wrote a little block of code as sort of an example, though I'm almost entirely sure there is a better way to accomplish this effect.


term.clear()
term.setCursorPos(1,1)
write("Key Testing Time")
local bLoopCheck = true
local bCtrlCheck = false
while bLoopCheck do
    local event,p1 = os.pullEvent()
    if event == "key" and p1 == 45 then -- 45 relates to the 'X' key
        bCtrlCheck = true
        os.queueEvent("ctrlchk") -- If bCtrlCheck is still true...
    elseif event == "char" and string.lower(p1) == "x" then
        bCtrlCheck = false
    elseif event == "ctrlchk" and bCtrlCheck == true then
        print("Ctrl+X pressed") -- ...Then trigger the event
        bCtrlCheck = false
        bLoopCheck = false -- End the loop
    end
end

Ctrl+X is the key combination that triggers the code, all it does for this example is loop through again, waiting for the next time Ctrl+X is pressed. Note that the 'ctrlchk' event will consistantly trigger after the 'char' event. If anybody knows of an easier / more efficient way of accomplishing this behavior, feel free to correct me.
5olstitium #18
Posted 18 June 2012 - 03:33 AM
BUNCHA INFO

So if you do os.pullEvent = os.pullEventRaw, what many people do to prevent Ctrl+T
you just reroute any os.pullEvent call to the direct os.pullEventRaw which does not check for the terminate event

Thanks for the explanation.
Keetster #19
Posted 01 July 2012 - 02:13 AM
I get an error when trying to run a program based off of this

Error is: bios:206: [string "function"]:7: unexpected symbol

This is line 7: event, 3, param2 = os.pullEvent("rednet_message") – 3 is the id of the sender, param2 is the string

The program is called function. Not sure if it matters.
MysticT #20
Posted 01 July 2012 - 02:55 AM
I get an error when trying to run a program based off of this

Error is: bios:206: [string "function"]:7: unexpected symbol

This is line 7: event, 3, param2 = os.pullEvent("rednet_message") – 3 is the id of the sender, param2 is the string

The program is called function. Not sure if it matters.
Already answered in the post you made about this.
Explorer #21
Posted 18 July 2012 - 07:14 AM
Thanks this really cleared my syntax confusion in a few of my programs
NeverCast #22
Posted 30 August 2012 - 12:51 AM
I know this is an old topic, but I feel this should be asked and why not on a forum post that is referenced from the wiki where others can see it.

If I filter an event with a string parameter to either pullEvent or pullEventRaw, will any event at the top of the stack/head of the queue ( However the events are implemented ) be destroyed if it does not match. What I mean is, if I use os.pullEvent("rednet_message"), press a key, then receive a message. Will that key press still be in the queue waiting for either a pullEvent("char"), pullEvent("key") or pullEvent(). OR will it be disposed and gone?

The reason I ask is if I'm waiting for a redstone signal, will rednet messages still be queued up. I know I could use a while loop without a filter. But it'd be good to know how filters handle non-matching events. If anyone can answer this it would be greatly appreciated.

Thanks, and sorry for the grave dig.

- NC
MysticT #23
Posted 30 August 2012 - 01:59 AM
I know this is an old topic, but I feel this should be asked and why not on a forum post that is referenced from the wiki where others can see it.

If I filter an event with a string parameter to either pullEvent or pullEventRaw, will any event at the top of the stack/head of the queue ( However the events are implemented ) be destroyed if it does not match. What I mean is, if I use os.pullEvent("rednet_message"), press a key, then receive a message. Will that key press still be in the queue waiting for either a pullEvent("char"), pullEvent("key") or pullEvent(). OR will it be disposed and gone?

The reason I ask is if I'm waiting for a redstone signal, will rednet messages still be queued up. I know I could use a while loop without a filter. But it'd be good to know how filters handle non-matching events. If anyone can answer this it would be greatly appreciated.

Thanks, and sorry for the grave dig.

- NC
They are removed from the queue, and then checked for the filter. So yes, they are deleted and lost. You should make a while loop like you said, or use parallel and wait for different events in each function. Is basically the same, but depending what you want to do one will be better thatn the other.
timothyhtime #24
Posted 20 September 2012 - 06:54 PM
I'm trying to recode the terminate event, but I don't want it to interfere with my program running. Is there a way to reprogram the terminate event without stalling the program with a pullEvent. maybe just a way to check if an event is present or what, I don't know, but some direction here would help. thanks.

EDIT: ok, so I've found a way to get around it, but what I'm looking for at this point is a way to check two events and ignore any other event. any suggestions?

EDIT2: well, I managed to come up with a less than desired, but fully functional (as far as I can tell) solution to my problem. for those of you who are interested in being able to do the same. here you go.


os.pullEvent = os.pullEventRaw
function termiFunc()
  -- your alternative terminate commands
  print("bye")
  sleep(3)
  os.shutdown()
end
function checkEvent()
  local e = os.pullEvent()
  if (e == "terminate") then
	termiFunc()
  elseif not (e == "timer") then
	checkEvent()
  end
end
for a = 0, 20 do
  os.startTimer(.5)
  checkEvent()
  print("next")
end
Noodle #25
Posted 20 September 2012 - 11:22 PM
I'm trying to recode the terminate event, but I don't want it to interfere with my program running. Is there a way to reprogram the terminate event without stalling the program with a pullEvent. maybe just a way to check if an event is present or what, I don't know, but some direction here would help. thanks.

EDIT: ok, so I've found a way to get around it, but what I'm looking for at this point is a way to check two events and ignore any other event. any suggestions?

EDIT2: well, I managed to come up with a less than desired, but fully functional (as far as I can tell) solution to my problem. for those of you who are interested in being able to do the same. here you go.


os.pullEvent = os.pullEventRaw
function termiFunc()
  -- your alternative terminate commands
  print("bye")
  sleep(3)
  os.shutdown()
end
function checkEvent()
  local e = os.pullEvent()
  if (e == "terminate") then
	termiFunc()
  elseif not (e == "timer") then
	checkEvent()
  end
end
for a = 0, 20 do
  os.startTimer(.5)
  checkEvent()
  print("next")
end
Nice code for a newbie!
A few things though
1: os.pullEvent = os.pullEventRaw protects from termination, no real use in this script.
2: 'While true do' is a better kind of loop.
Helpful code
while true do
 e = os.pullEvent()
 if e == "terminate" then
   termiFunc()
 end
end
That will loop repeatedly, no need for the checkEvent().
Cranium #26
Posted 20 September 2012 - 11:26 PM
Calling a function within the function is a bad practice. That will cause an overflow error, because of recursion. Noodle is right, a while loop is better.
Zambonie #27
Posted 28 December 2012 - 09:45 AM
To me,its like an more advanced way of saying:


local input = read()
if input == "1" then

Altleast I learned it….
theoriginalbit #28
Posted 28 December 2012 - 01:34 PM
  • "char" when text is typed on the keyboard. Argument is the letter typed.
  • "key" when a key is pressed on the keyboard. Argument is the numerical keycode.
  • "timer" when a timeout started by os.startTimer() completes. Argument is the value returned by startTimer().
  • "alarm" when a time passed to os.setAlarm() is reached. Argument is the value returned by setAlarm().
  • "redstone" when the state of any of the redstone inputs change.
  • "disk" or "disk_eject" when a disk is entered or removed from an adjacent disk drive. Argument is the side.
  • "rednet_message" when a message is received from the rednet API.


You missed 8…

http://www.computercraft.info/wiki/Os.pullEvent#Event_Types
immibis #29
Posted 28 December 2012 - 02:03 PM
  • "char" when text is typed on the keyboard. Argument is the letter typed.
  • "key" when a key is pressed on the keyboard. Argument is the numerical keycode.
  • "timer" when a timeout started by os.startTimer() completes. Argument is the value returned by startTimer().
  • "alarm" when a time passed to os.setAlarm() is reached. Argument is the value returned by setAlarm().
  • "redstone" when the state of any of the redstone inputs change.
  • "disk" or "disk_eject" when a disk is entered or removed from an adjacent disk drive. Argument is the side.
  • "rednet_message" when a message is received from the rednet API.


You missed 8…

http://www.computercraft.info/wiki/Os.pullEvent#Event_Types
It should say "the most common events" then.

terminate is invisible to most programs as it's handled inside os.pullEvent.
Same for http_success and http_failure which are handled inside http.get.
peripheral and peripheral_detach should probably be included.
mouse_click, mouse_scroll and mouse_drag were added after the post was written.
Zudo #30
Posted 03 July 2013 - 01:34 PM
// Off topic

How did you get that title?
Bordo_Bereli51 #31
Posted 04 July 2013 - 02:10 PM
A very good guide to understand. Thank you!
RichyRocker423 #32
Posted 18 July 2013 - 12:14 AM
Is there a way to have os.pullevent look for 2 events? Like I'm trying for monitor touch and redstone, so that they will both end the waiting period os.pullevent induces. If I have to I'll set all the other types off but I don't know how. Please respond soon.
LBPHacker #33
Posted 18 July 2013 - 02:25 AM
-snip-
No way of making os.pullEvent listen to more than one events. You'll have to check the event yourself.
while true do
    -- * puts everything returned by .pullEvent into a table
    local eventData = {os.pullEvent()}
    -- * eventData[1] will contain the name of the event
    if eventData[1] == "monitor_touch" then
        -- * do something with the other indexes of eventData
        -- * unocomment the block below if you have no idea what to do with eventData

        --[[ local side = eventData[2]
        local touchX = eventData[3]
        local touchY = eventData[4]
        -- * now you have side, touchX and touchY
        ]]

        -- * ...
    elseif eventData[1] == "redstone" then
        -- * ...
    end
end
See? It'll check the first parameter returned by .pullEvent, and that contains the name of the event. This:
-- * ...
means that you have to insert your code there.
CyborgAlex #34
Posted 30 July 2013 - 01:03 PM
Thanks For help
Eunomiac #35
Posted 11 August 2013 - 03:08 PM
One of the problems I have with using events is that, once you tell the program to listen for an event, it has to stop in its tracks until the event occurs. Is there a way to get a program to listen for an event, but continue what it's doing until the event is triggered? I.e. "Keep doing what you're doing until you receive a 'stop' message over rednet. When you do, no matter where you are in the program, break and immediately do [X]." (A specific example might be an emergency RedNet kill switch for a lengthy 'while true do … end' loop, or an alarm event that you want to break/terminate your program at the instant the alarm fires.)

Should I look for answers in the "parallel" API, using alternating yields to manage the coroutines? Or is there a better/cleaner way to go about this?
LBPHacker #36
Posted 11 August 2013 - 04:38 PM
-snip-
Yup, using coroutines in that case is possible (or a rednet.receive with a short timeout at the beginning of the endless loop - most of those things can be solved without using coroutines though). BUT:

no matter where you are in the program
Even with coroutines, only one piece of code runs at a time. In order to eg. check for messages over rednet, the program (or the coroutine) which has to stop on a "kill" message must yield - that is, call os.pullEvent and the like.
Onionnion #37
Posted 24 November 2013 - 05:18 PM
Discussion on this still going on through August? Neat. If any of this is out of date let me know, I shall fix if needed. I've been away, yes, but I'm taking some dips back here.
Fedkiv #38
Posted 07 December 2013 - 07:39 AM
Ive been reading about mission building and I see a lot of references to "Flags".

From what I understand they are a true false condition that can trigger other events.

What I dont understand is how to add one in the mission editor. Nothing I can find says "add flag" or anything like that.

Im sure it is a simple procedure but I cant seem to figure it out.

Thanks
Galbi3000 #39
Posted 10 March 2015 - 01:43 AM
I know this is an old thread but I am having difficulty with os.pullEvent()!

I set a timer to activate every 2 seconds. Let's say that gets given an ID of 130.
I then start a communication function to communicate with a target computer. That function sets another timeout timer of 4 seconds. Let's say that gets the ID of 131.

The loop for the communication function receives the pullEvent trigger for timer 130, ignores that as it should, then another 2 seconds later gets the pullEvent trigger for timer 131. The function calls os.cancelTimer(131) after the correct ID was found on the timer event, also calls that if the function gets the response message before that timer is triggered.

The communication function works fine, does what is expected every time.

The problem occurs in the main program loop. It does a pullEvent to test for keyboard input and that 2 second timer (ID 130 in this example). It gets the key events but never gets the timer event!

Here is the code with unimportant parts taken out…


local modem = peripheral.wrap("back")
modem.open(555)
local count = 0
term.clear()
local computerInRange = true
local myTimer = os.startTimer(1)

-- Function transmit
--
-- Transmits a message and gets a response.
-- If no response in 4 seconds, returns false.
local function transmit(port, message)
  modem.transmit(port, 555, message)
  local timeout = os.startTimer(4)

  while true do
    local e, p1, p2, p3, p4, p5 = os.pullEvent()

    if e == "timer" then
	  if p1 == timeout then
	    os.cancelTimer(timeout)
	    return false
	  end
    end

    if e == "modem_message" and p2 == 555 then
	  os.cancelTimer(timeout)
	  return p4
    end
  end
end

local function updateDisplay()
  -- Updates the status display
end

-- First gets status data then continues:

updateDisplay()
while true do
  local event, p1, p2, p3, p4, p5 = os.pullEvent()

  if event == "timer" then
    if p1 == myTimer then
	  -- Update display
	  updateDisplay()
	  -- Poll for other computer
	  reply = transmit(123, "Hello?")
	  if reply == false then
	    computerInRange = false
	  elseif reply == "Hi!" then
	    computerInRange = true
	  else
	    term.clear()
	    print("Unexpected response!")
	    print("Checking in.")
	    print("Remote computer: "..reply)
	    break
	  end
    end

    if event == "key" and p1 == 38 then
	  -- Do key L event stuff
    end

    if event == "key" and p1 == 24 then
	  -- Do key O event stuff
    end
end

os.cancelTimer(myTimer)

It is run on a portable computer which is why it needs to check if the target computer is in range while also updating the display.
Edited on 10 March 2015 - 12:54 AM
Galbi3000 #40
Posted 10 March 2015 - 01:58 AM
Never mind, I have seen my mistake after going through the simplified code to tidy it in the posting! lol

It's a missing 'end' statement that ComputerCraft scripting missed! No errors given for the missing end! :P/>
Edited on 10 March 2015 - 01:00 AM
Geforce Fan #41
Posted 10 March 2015 - 02:08 AM

function os.pullEvent( _sFilter )
  local event, p1, p2, p3, p4, p5 = os.pullEventRaw( _sFilter )
  if event == "terminate" then
	print( "Terminated" )
	error()
  end
  return event, p1, p2, p3, p4, p5
end
Oh dear god, os.pullEvent() is programmed very poorly in that case.
It should be:

local event = {os.pullEventRaw( _sFilter )}
  if event[1] == "terminate" then
    error ( "Terminated" )
  end
  return unpack(event)
end
:\
Edited on 10 March 2015 - 01:09 AM
Galbi3000 #42
Posted 10 March 2015 - 02:09 AM
Nope, even with that missing end added it still fails. After a few successful triggers the myTimer (ID 130 from previous example) stops triggering! Any ideas?
Bomb Bloke #43
Posted 10 March 2015 - 02:11 AM
FYI, when a timer completes and throws it's associated event, that's it - it stops, having completed its task. It doesn't automatically start counting again in preparation to throw another event, so there's no need to os.cancelTimer() it.
Galbi3000 #44
Posted 10 March 2015 - 02:19 AM
FYI, when a timer completes and throws it's associated event, that's it - it stops, having completed its task. It doesn't automatically start counting again in preparation to throw another event, so there's no need to os.cancelTimer() it.
So I should be setting up a new timer after each event to have it repeat?

E.G.


-- Do something every 5 seconds
local myTimer = os.startTimer(5)
while true do
  local event, p1, p2, p3, p4, p5 = os.pullEvent()
  if event == "timer" and p1 == myTimer then
	-- Do something
	myTimer = os.startTimer(5)
  end
  -- Process other events
end
Edited on 10 March 2015 - 01:19 AM
Bomb Bloke #45
Posted 10 March 2015 - 02:22 AM
If you want it repeated, that's pretty much exactly how you'd do it.

But think about whether you'd want to "do something" first then restart the timer, or restart the timer first then "do something". Code execution takes time too.
Galbi3000 #46
Posted 10 March 2015 - 02:25 AM
Thank you for your quick replies :)/>

I will let you know if it works.
Galbi3000 #47
Posted 10 March 2015 - 02:47 AM
Only triggering twice! :wacko:/>

I added a counter in the display update function to show how many times the function is called. It gets called three times.

Here is the simplified code as it stands now…


local modem = peripheral.wrap("back")

local count = 1

modem.open(555)

term.clear()

local computerInRange = true

-- Function transmit
--
-- Transmits a message and gets a response.
-- If no response in 4 seconds, returns false.

local function transmit(port, message)
	modem.transmit(port, 555, message)
	local timeout = os.startTimer(4)
	
	while true do
		local e, p1, p2, p3, p4, p5 = os.pullEvent()
		
		if e == "timer" then
			if p1 == timeout then
				return false
			end
		end
		
		if e == "modem_message" and p2 == 555 then
			return p4
		end
	end
end

local function checkIn()
	local reply = transmit(1, "Hello?")
	if reply == false then
		computerInRange = false
		return true
	elseif reply == "Hi!" then
		return true
	end
	return false
end

local function updateDisplay()
	term.setCursorPos(1,1)
	term.write(tostring(count))
	count = count + 1
end

updateDisplay()

while true do
	local myTimer = os.startTimer(2)
	local event, p1, p2, p3, p4, p5 = os.pullEvent()
	
	if event == "timer" then
		if p1 == myTimer then
			updateDisplay()
			if checkIn() == false then break end
		end
	end

	-- Other events
end


PS I am a seasoned ex-programmer. I had over 10 years professional C/C++ programming before I was forced to quit for family reasons ;)/>


EDIT - I ran the above code as a test program and the timer was triggered 3 times (the updateDisplay was called 4 times) then stopped!
I adjusted the value from 2 seconds to 4 seconds and it works fine!
Edited on 10 March 2015 - 02:06 AM
Galbi3000 #48
Posted 10 March 2015 - 03:19 AM
Ok, final post. I have solved the problem! Even though that simplified version worked with a longer pause my main program still failed after 2 triggers!

The reason was the transmit timeout timer! If the target computer responds fine, the function returned but that timer was still active. For some reason, when it triggered, it stopped the main timer from working!

To solve it I put back the os.cancelTimer() call in one place…


local function transmit(port, message)
	modem.transmit(port, 555, message)
	local timeout = os.startTimer(4)
	
	while true do
		local e, p1, p2, p3, p4, p5 = os.pullEvent()
		
		if e == "timer" then
			if p1 == timeout then
				return false
			end
		end
		
		if e == "modem_message" and p2 == 555 then
			os.cancelTimer(timeout)
			return p4
		end
	end
end

Now my program works fine, even with the main timer value set to 1 second :)/>
Edited on 10 March 2015 - 02:20 AM
Bomb Bloke #49
Posted 10 March 2015 - 04:40 AM
For some reason, when it triggered, it stopped the main timer from working!

In your main program loop down the bottom of the script, you create a new timer and store the id in "myTimer" whenever any event occurs (eg, transmit()'s timer expires, the user presses a key, a modem_message event happens, etc…). You should only be recreating that timer when it expires, because the old timers that you aren't bothering to catch as a result of all this are themselves leading to new timers being created, and so on…
DaKillerBear1 #50
Posted 16 May 2015 - 02:04 PM
I have a problem, how can I make it so that if there is no key being pressed at the moment the program just continues?
theoriginalbit #51
Posted 16 May 2015 - 03:57 PM
I have a problem, how can I make it so that if there is no key being pressed at the moment the program just continues?
queue a timer with 0 seconds before you pull events


--# start a timer that will complete immediately
local timeout = os.startTimer(0)

--# get the next event in the queue
local event = { os.pullEvent() }

--# check if the event was a keypress, otherwise ignore the event
if event[1] == "key" then
  -- do stuff
end
Waitdev_ #52
Posted 17 May 2015 - 10:24 AM
i know a lot about os.pullevent and i was looking for even more ways to use it, but really nice how you made something to teach others. now even i have a reference if i forget anything :)/>