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

button controlled turtle mining

Started by Robert1707, 26 June 2015 - 12:25 PM
Robert1707 #1
Posted 26 June 2015 - 02:25 PM
Hi

I am in the proccess of coding a mining program using turtle. At the moment I have finished the mining aspect of the program. Further the turtles that do the mining are controlled by a main computer that (using direwolf20 's button API) displays buttons that can be used to start the mining process. What I have happen is that the user selects a distance they want to mine using 4 different presets. They then select the turtle that is to carry out the mining. What happens is that when the turtle is selected the button turns green indicating that it is mining. I have it set to send the mining instructions wirelessly.

One thing that I have not been able to do is that when the turtle is done mining it should send a signal back to the main computer to toggle the button back to a red state. This would indicate that the turtle has finnished its mining. I have looked into my code and have found out that the reason why it doesn't get the signal is that the os.pullEvent() function waits for an event before moving on to the next line of code. I need to make this pullEvent temporary. Maybe happen for a second then for another second it will wait for an incoming signal from the turtle. I have the following code:


while true do
   getClick()
   id, Report = rednet.receive(1)
   if Report == "done" then
	 if id == 5 then
	   button.toggleButton("Miner1")
	 elseif id == 6 then
	   button.toggleButton("Miner2")
	 elseif id == 7 then
	   button.toggleButton("Miner3")
	 elseif id == 8 then
	   button.toggleButton("Miner4")
	 end
   end
end

For anyone familiar with direwolfs button API the getClick function will be familiar however anyone that isn't what is basically does is wait for a monitor touch. The code above runs the getClick (which is where the problem lies with the os.pullEvent()) and then is should receive a message from the turtle with a string reading "done" it then checks which turtle it came from and toggles the appropriate button back to red. I have worked around this by having another computer that has a display that tells me if the turtle is done mining. getClick code below:



function getClick()
   event,side,x,y = os.pullEvent("monitor_touch")
   button.checkxy(x,y)
end

I was wondering if there is a way in which I can edit the getClick function or the API itself so as the loop from the main code will continue to run. I did find a post ( http://www.computerc...t-with-timeout/ ) that explained how to do it but the coder put the main code into the button API code and ran the program from that rather than use it as an API. I, being new to lua programming and computercraft, don't understand how to incorporate the code of that post into my main code using an API.

Another thing that i would love to do is have it so that buttons on the screen would be used to set the mining value. ie there would be a "+" and "-" button that would be used to set the value. I got this to partly work in tests however I did not know how to display the value on the screen. I got it to display the value however pressing the add and subtract buttons would not refresh the screen and so the value displayed would not update. But I have worked around that by having preset mining values that the miners could run.

Any help would be much appreciated.
Thanks
Robert

My entire code http://pastebin.com/nctbSbcj
Bomb Bloke #2
Posted 26 June 2015 - 03:30 PM
In regards to your event handling, it may help to understand that most any time a ComputerCraft system is waiting for anything, what it'll really be waiting for is an event. New events are added to the end of the system's event queue as they occur, and later pulled from the front of it (usually using os.pullEvent()). When you do eg os.pullEvent("monitor_touch"), the system starts pulling events from the front of the queue until it finds what it wants. If it finds events of other types, it discards them. If it empties the queue, then it sits and waits until more events appear.

With that in mind, the source of rednet.receive() loosely goes like this:

function rednet.receive(timer)
	local timer = os.startTimer(timer)

	while true do
		local event, par1, par2, par3 = os.pullEvent()

		if event == "timer" and par1 == timer then
			return nil
		elseif event == "rednet_message" then
			return par1, par2, par3
		end
	end
end

When you do rednet.receive(1), this code will either run until a second passes, or until a rednet_message event is found in the queue (whichever happens first). If any other events occur during that time (eg monitor_touch), they're entirely ignored - they're removed from the queue, too, so they can't be pulled again by your getClick() function.

Likewise, even if you incorporate a timer into getClick(), you've got the same problem in reverse - any rednet_message events that arrive while getClick()'ing will get discarded!

The solution is to simply rig your main loop to pull all events itself, without using a filter, and have it inspect each one to see what (if anything) it should do with it. In this manner, you don't even need a time-out system, because you're always waiting for whatever events you happen to want.

Eg:

while true do
	local event, par1, par2, par3 = os.pullEvent()
	
	if event == "rednet_message" and par2 == "done" then
		if par1 == 5 then
			button.toggleButton("Miner1")
		elseif par1 == 6 then
			button.toggleButton("Miner2")
		elseif par1 == 7 then
			button.toggleButton("Miner3")
		elseif par1 == 8 then
			button.toggleButton("Miner4")
		end
	elseif event == "monitor_touch" then
		button.checkxy(par2,par3)
	end
end
Edited on 26 June 2015 - 01:32 PM
Robert1707 #3
Posted 28 June 2015 - 10:00 PM
Thanks a lot for the solution. It will be great to finally get my code to do what i want it to so. Just one question. Where you have used par1, par2, etc could I use any other name just for the sake of remembering what they do.
Thank for the help. I'll finish my code and then I can post it for anyone to use.
Thanks again
Bomb Bloke #4
Posted 29 June 2015 - 12:54 AM
Just one question. Where you have used par1, par2, etc could I use any other name just for the sake of remembering what they do.

Bear in mind that "what they do" will vary depending on what sort of event gets pulled. For example, if "event" is "rednet_message", then "par2" will contain the message; if "event" is "monitor_touch", then "par2" will be the column that was clicked.

But yes, it's entirely up to you as to what you call them.
Robert1707 #5
Posted 29 June 2015 - 04:55 PM
Thanks a lot. You've been a great help. I just tested the code and it works like a dream. Just putting the finishing touches on it and then I'll post it.
Robert1707 #6
Posted 30 June 2015 - 11:59 AM
Hi

I have another question. I am setting up the system so that the main controller (which is whats coded for above) send a message via lan cable to a "booster computer" so because of the range of wireless modems. I have made it so that the sending of the message is passed on to the correct turtle and it runs it. The only problem is that with the pullEvent function I don't know how to set it up so that the it receives the id of the turtle from a different computer. I'll ellaborate:


rednet.open("right")
rednet.open("left")

while true do
  id, Compid, message = rednet.receive()
  if id == 23 then
    rednet.send(Compid, message)
  else
    rednet.send(23, id, message)
  end
end

above is the code for my booster computer. It is checking whether the id is from the main computer (first part of the if) and if it is it send the message with the id that was sent with it (I edited the main code so that it would not only send a message but the id of the turtle the message is meant for). That part works fine. The next part of the code i think works fine. Basically if the id is not the main computer it has to come from one of my turtles so I have set them up to send the message they need to send with a blank field for the Compid section. The booster computer will thus send the id of the computer that sent the message to it (the turtle) to the main computer.

The problem, however, come in when the message gets to the main computer. The code is checking the id of the computer that is sending the message (I think) which would be the booster in this case. I need it to check the message for the id instead as the booster send the turtle's id in a message.

Let me give an example: I will click on the button to make the turtle mine 50 blocks. I then click on the turtle's button. A message is sent by the main computer to the booster computer (using rednet.send(22, Turtleid, message) the 22 is the booster computer id and the turtleid is set earlier in the code). The booster computer receives the messages using variables (id, Compid, message = rednet.receive()). the booster computer then passes on the message to the turtle (using rednet.send(Compid, message) the Compid is the Receved Turtleid) That part all works fine. When the turtle is done mining it sends a message "done" to the booster (using rednet.send(22, " ", "done")) the blank space is just so that "done" doesn't end up in the Compid field (I used the same receiving variables as before). I think this code also works but I haven't tested it fully. The part i don't know how to edit is the part in the main computer (the code given to me above) as it is waiting for an event not a rednet.receive(). I need it to receive a message from the booster computer containing the id of the turtle that is done mining as well as the message done.

I don't know how well I have explained myself. I hope it is understable.

Again any help will be much appreciated. Thanks
The_Cat #7
Posted 30 June 2015 - 12:46 PM
Hi

I have another question. I am setting up the system so that the main controller (which is whats coded for above) send a message via lan cable to a "booster computer" so because of the range of wireless modems. I have made it so that the sending of the message is passed on to the correct turtle and it runs it. The only problem is that with the pullEvent function I don't know how to set it up so that the it receives the id of the turtle from a different computer. I'll ellaborate:


rednet.open("right")
rednet.open("left")

while true do
  id, Compid, message = rednet.receive()
  if id == 23 then
	rednet.send(Compid, message)
  else
	rednet.send(23, id, message)
  end
end

If I understand what you basically want is to send the ID of the turtle which is received from the "main" computer which then sends this to the "booster computer".

If this is correct then you can simply send a table through to the "booster computer", so when the message is received to the booster you can do somthing like…

id, message = os.pullEvent()
print(message[1]) --# 1 Could be the ID
print(message[2]) --# 2 Could be the message to the turtle
rednet.send(message[1], message[2])
Edited on 30 June 2015 - 11:02 AM
Bomb Bloke #8
Posted 30 June 2015 - 12:59 PM
If you're on at least ComputerCraft 1.6, there's actually a bundled script available to all computers that deals with this scenario - repeat. You should pretty much be able to just tell your "booster" computer to run that, and the rest should sort itself out - the "server" computer will see the messages it gets from the "booster" computer as if they came from the turtle's ID, no extra work required.
Robert1707 #9
Posted 30 June 2015 - 02:10 PM
Oh. Okay thanks. I'll check it out
Robert1707 #10
Posted 30 June 2015 - 02:18 PM
I attempted to use the repeat program but it didn't work. Is there anything in particular that I need to do. I have made a program that runs it at startup (using shell.run("repeat")) The computer is displaying that it has messages that are being repeated but the turtle is not carrying out any function.
Bomb Bloke #11
Posted 30 June 2015 - 02:29 PM
Can't really comment much on that without seeing the code involved, but you remembered to have the turtle open a side for rednet usage, yes?
Robert1707 #12
Posted 30 June 2015 - 03:02 PM
yes I have. Below find the code for my turtle and controller:

Pardon the long code blocks. Pastebin keeps removing my pastes.

Mine Controller

os.loadAPI("button")
m = peripheral.wrap("top")
m.clear()

f = false
H = false
T = false
FH = false

M1 = false

Turtleid = 0

function fillTable()
   button.setTable("Miner1", Miner1, 3,13,3,5)
   button.setTable("Miner2", Miner2, 3,13,7,9)
   button.setTable("Miner3", Miner3, 3,13,11,13)
   button.setTable("Miner4", Miner4, 3,13,15,17)
  
   button.setTable("Move", Move1, 15,25,3,17)
   button.setTable("Move Reset", MoveReset, 15,25,19,20)
  
   button.setTable("50", Fifty, 30,38,3,5)
   button.setTable("100", Hundred, 30,38,7,9)
   button.setTable("200", TwoHund, 30,38,11,13)
   button.setTable("500", FiveHund, 30,38,15,17)
   button.setTable("Reset", Reset, 30,38,19,20)
  
   button.screen()
end

function Check()
  if f == true then
	button.toggleButton("50")
	f = false
	rednet.send(Turtleid,"mine50")
  elseif H == true then
	button.toggleButton("100")
	H = false
	rednet.send(Turtleid,"mine100")
  elseif T == true then
	button.toggleButton("200")
	T= false
	rednet.send(Turtleid,"mine200")
  elseif FH == true then
	button.toggleButton("500")
	FH = false
	rednet.send(Turtleid,"mine500")
  end
end

function MoveCheck()
  if M1 == true then
	button.toggleButton("Move")
	M1 = false
	rednet.send(Turtleid, "move")
  end
end  

function Miner1()
   button.toggleButton("Miner1")
   Turtleid = 6
   Check()
   MoveCheck()
end

function Miner2()
  button.toggleButton("Miner2")
  Turtleid = 7
  Check()
  MoveCheck()
end

function Miner3()
  button.toggleButton("Miner3")
  Turtleid = 8
  Check()
  MoveCheck()
end

function Miner4()
  button.toggleButton("Miner4")
  Turtleid = 9
  Check()
  MoveCheck()
end

function Fifty()
   button.toggleButton("50")
   f = true
end

function Hundred()
   button.toggleButton("100")
   H = true
end

function TwoHund()
  button.toggleButton("200")
  T = true
end

function FiveHund()
  button.toggleButton("500")
  FH = true
end

function Reset()
  button.flash("Reset")
  if f == true then
	button.toggleButton("50")
	f = false
  elseif H == true then
	button.toggleButton("100")
	H = false
  elseif T == true then
	button.toggleButton("200")
	T = false
  elseif FH == true then
	button.toggleButton("500")
	FH = false
  end
end

function Move1()
  button.toggleButton("Move")
  M1 = true
end

function MoveReset()
  button.flash("Move Reset")
  if M1 == true then
	button.toggleButton("Move")
	M1 = false
  end
end

rednet.open("left")
fillTable()
button.heading("Miner Controller")

while true do
  local event, Turtleid, par2, par3 = os.pullEvent()

  if event == "rednet_message" and par2 == "done" then
	if Turtleid == 6 then
	  button.toggleButton("Miner1")
	elseif Turtleid == 7 then
	  button.toggleButton("Miner2")
	elseif Turtleid == 8 then
	  button.toggleButton("Miner3")
	elseif Turtleid == 9 then
	  button.toggleButton("Miner4")
	end
  elseif event == "monitor_touch" then
	button.checkxy(par2,par3)
  end
end

turtle

Full = false
NeedTorch = false
CoalNeeded = math.ceil((3000-turtle.getFuelLevel())/80)
UserCount = 0
Torch = 8
NeedFuel = false
MineCount = 0
iCount = 0
iDig = false

function column()
  turtle.digUp()
  turtle.up()
  turtle.digUp()
  turtle.down()
end

function isBlock()
  iDig = turtle.detect()
  while iDig == true do
	turtle.dig()
	iDig = turtle.detect()
  end
end

function Face()
  turtle.dig()
  isBlock()
  turtle.forward()
  column()
  turtle.turnLeft()
  turtle.dig()
  isBlock()
  turtle.forward()
  column()
  turtle.turnLeft()
  turtle.turnLeft()
  isBlock()
  turtle.forward()
  turtle.dig()
  isBlock()
  turtle.forward()
  column()
  turtle.turnLeft()
  turtle.turnLeft()
  isBlock()
  turtle.forward()
  turtle.turnRight()
end

function InventoryFull()
  if turtle.getItemCount(14) >= 1 then
	Full = true
  else
	Full = false
  end
end

function dump()
  for DumpCount = 1,14 do
	turtle.select(DumpCount)
	turtle.drop()
  end
  turtle.select(1)
end

function Needed()
  if Full == true or NeedTorch == true or NeedFuel == true then
	ReturnHome(MineCount)
	turtle.turnLeft()
	dump()
	if NeedTorch == true then
	  turtle.up()
	  turtle.select(16)
	  turtle.suck(turtle.getItemSpace(16))
	  turtle.select(1)
	  if NeedFuel == true then
		turtle.up()
		turtle.select(14)
		turtle.suck(CoalNeeded)
		turtle.refuel()
		turtle.select(1)
		turtle.down()
		turtle.down()
	  else
		turtle.down()
	  end
	end
	turtle.turnLeft()
	ReturnMine(MineCount)
  end
end

function CheckTorch()
  if turtle.getItemCount(16) < 2 then
	NeedTorch = true
  else
	NeedTorch = false
  end
end

function CheckFuel()
  if turtle.getFuelLevel() < UserCount + 100 then
	NeedFuel = true
  else
	NeedFuel = false
  end
end

function Checks()
  CheckFuel()
  CheckTorch()
  InventoryFull()
end

function ReturnHome(blocks)
  turtle.turnLeft()
  turtle.turnLeft()
  for ReturnCount = 1, blocks do
	turtle.forward()
  end
end

function ReturnMine(blocks)
  for ReturnCountM = 1, blocks do
	turtle.forward()
  end
end

function DoneMining()
  turtle.turnLeft()
  turtle.select(16)
  turtle.place()
  turtle.select(1)
  turtle.turnLeft()
  for DoneMiningC = 1, UserCount do
	turtle.forward()
  end
  turtle.turnLeft()
  dump()
  turtle.turnLeft()
  ChangePeriW()
  rednet.send(1,"done")
end

function PlaceTorch()
  turtle.turnLeft()
  turtle.select(16)
  turtle.place()
  turtle.select(1)
  turtle.turnRight()
end

function mine()
  ChangePeri()
  for iCount = 1, UserCount do
	Checks()
	Needed()
	Face()
	MineCount = MineCount + 1
	if Torch >= 8 then
	  PlaceTorch()
	  Torch = 0
	else
	  Torch = Torch + 1
	end
  end
  DoneMining()
end

function Move()
  turtle.turnLeft()
  turtle.turnLeft()
  for MC = 1, 6 do
	turtle.forward()
  end
  rednet.send(1, "done")
end

function ChangePeri()
  turtle.select(15)
  turtle.equipRight()
  peripheral.wrap("Right")
  turtle.select(1)
end

function ChangePeriW()
  ChangePeri()
  rednet.open("right")
end

rednet.open("right")

while true do
  id,message = rednet.receive()
  if id == 1 then
	if message == "mine50" then
	  UserCount = 50
	  mine()
	elseif message == "mine100" then
	  UserCount = 100
	  mine()
	elseif message == "mine200" then
	  UserCount = 200
	  mine()
	elseif message == "mine500" then
	  UserCount = 500
	  mine()
	elseif message == "move" then
	  Move()
	end
  end
end

the booster is just
shell.run('repeat") as a startup program
Edited on 30 June 2015 - 01:03 PM
Bomb Bloke #13
Posted 01 July 2015 - 12:22 AM
Ok. What if you change the turtle's code so that it prints information about any messages it receives? What's it say?

while true do
  id,message = rednet.receive()
  print(id,message)
  if id == 1 then
  .
  .
  .

Pastebin will probably leave your pastes alone if you experiment with their titles. They've got some sort of automated spam removal system, and they don't give two hoots about false positives.
Robert1707 #14
Posted 01 July 2015 - 02:51 PM
-scratch-

Okay. I figured it all out. The code it running beautifully. Thanks very much for your help. If you wouldn't mind and have some time to spare could you answer another question of mine:
I am now working on a turtle program that would check if the chests used to hold the items for the mining turtle have items in them. I have tried to use the turtle.suck() command in order to check this as I see that it outputs a Boolean value. I tried to use that to set a variable as a Boolean by doing this:
Items = turtle.suck()

I tested it and it didn't work. I was wondering if there is a way that I could get the turtle.suck() command to set a variable as a Boolean flag or if there is another way to check if there are items in the chest.

If all else fails I suppose I could make the turtle suck up an item and it would then check the space in that slot using turtle.getItemSpace(). If that equals 64 then that means that the chest are full.

Thanks again for all the help you have given me Bomb Bloke and thanks to THE_CAT for helping me with the code for the relay even though I didn't need it in the end.
Edited on 01 July 2015 - 01:08 PM
Bomb Bloke #15
Posted 01 July 2015 - 04:00 PM
I have tried to use the turtle.suck() command in order to check this as I see that it outputs a Boolean value. I tried to use that to set a variable as a Boolean by doing this:
Items = turtle.suck()

I tested it and it didn't work.

Nevertheless, that's pretty much how you'd do it. You didn't go and put that in a loop or something, did you? What did it do as opposed to "working"? What exactly is making you think it isn't working?
Robert1707 #16
Posted 07 July 2015 - 10:48 AM
Sorry for the late reply. I have been on a trip for a while.

I figured out my issue. What happened is that the turtle was not preforming the suck task each time it ran as it only ran the turtle.suck in the beginning of the program. I edited the program so that each time an if statement was run it would do the turtle.suck (I did this by making the turtle.suck the argument for the if ie IF turtle.suck() == false then …)

Is there a way of making a programs that will gather info from the user and use it in another program. I am looking to make my code more user friendly by removing the editing portion of the programs (for the ID's and such). What I want to happen is that the User should run a program in which they will enter the ID of the turtles and the main computer. These will then be used in my main code. I don't want to put this in my main code as it means that it would need to be done each time the turtle is sent to mine. (or each time the game starts up). I currently have the ID's hard coded in the code so the user would have to edit the program find the lines that need to be edited and edit them. Quite laborious.

Also I don't know how familiar you are with Direwolf20's Button API but one thing that I don't know how to do is make a blank button. What needs to happen is you need to give you button a name. But it prints the name. I need to toggle different buttons on and off independently of the others so it they have the same name then I don't believe I can do that. I basically need to find a way to have a blank button that just changes colour. I have got around the button naming in the past by numbering buttons ie Miner1, Miner2 etc
Edited on 07 July 2015 - 09:16 AM
KingofGamesYami #17
Posted 07 July 2015 - 02:48 PM
Also I don't know how familiar you are with Direwolf20's Button API but one thing that I don't know how to do is make a blank button. What needs to happen is you need to give you button a name. But it prints the name. I need to toggle different buttons on and off independently of the others so it they have the same name then I don't believe I can do that. I basically need to find a way to have a blank button that just changes colour. I have got around the button naming in the past by numbering buttons ie Miner1, Miner2 etc

I dislike DW20's API, I prefer to use either my own or Lyqyd's touchpoint, depending on if I'm using monitors or not.

But, you could try naming the button a string with just spaces in it (eg. " ").
Edited on 07 July 2015 - 12:48 PM
Robert1707 #18
Posted 08 July 2015 - 06:02 PM
Hi

Would using the spaces give unique Names? I need to make 8 buttons that are blank but they need to toggle independently.
KingofGamesYami #19
Posted 08 July 2015 - 06:20 PM
If they have different lengths of spaces, like so:

""
" "
"  "
"   "
"    "
"     "
"      "
"       "

…yes, they are unique.
Robert1707 #20
Posted 09 July 2015 - 03:23 PM
Cool thanks
Robert1707 #21
Posted 12 July 2015 - 06:45 PM
Hi. Sorry to bother again but I need a bit more help. I have added to the turtle code a section that checks if the chests are full and if so sends a signal to the controller computer. The code I believe is working fine except that I get an error: rednet :47: vm error: java.lang.ArrayIndexOutOfBoundsException

This is the code I am using for the checks


function isCoal()
    if turtle.suck(1) == false then
        rednet.send(25, "cFalse")
        isCoal()
    else
        turtle.drop()
        rednet.send(25, "cTrue")
    end
end

I use a similar code to check the torches chest. The idea behind calling the function again is that the turtle will keep checking the chest until the chest gets restocked. This prevents the turtle from continuing the mining until it has the required items to do so. I think that java is giving this error because it thinks that the turtle is caught in an infinite loop.

Could you suggest a course of action or a fix for this.

Thanks
KingofGamesYami #22
Posted 12 July 2015 - 11:59 PM
I think that java is giving this error because it thinks that the turtle is caught in an infinite loop.

Close. Actually, it's giving you this error because you have more than 256 function calls before any of them end.

TL;DR: Making a function call itself makes this error occur. Instead, use a loop.

local function isCoal()
  while not turtle.suck(1) do
    rednet.send( 25, "cFalse" )
    sleep(0)
  end
  turtle.drop()
  rednet.send( 25, "cTrue" )
end

I believe this will do what you want, while not causing a stack overflow nor a too long without yielding error.
Robert1707 #23
Posted 13 July 2015 - 12:03 PM
Okay thanks. I'll implement it now
Robert1707 #24
Posted 18 July 2015 - 08:04 PM
For anyone interested I have finished my code and posted it. Check it out with the link below:

http://www.computercraft.info/forums2/index.php?/topic/24027-more-turtle-mining-early-and-later-game-mining-wireless-miner/