Scrolling Marquee(s) with Multiple Monitors
——————————————————————–

I've had this program for awhile and decided to post it.

This can take an unlimited number of monitors (one monitor per computer), and automatically scrolls as many lines of text as you'd like from one monitor to the next. At the end of the last monitor, that line will go back to the first monitor, making an infinite loop. The speed of each line can also be customized. Everything can be configured in a couple text files.


PICTURES:

Spoiler




CODE:

You will need to tweak these files with the appropriate computer id's and what text you want to scroll. Use the code I gave as an example… it should be pretty self-explanatory. Instructions are also put in the comments at the top of the 'scrollMaster' and 'scrollSlave' programs.

The first three files go on the 'Master' computer, which will be attached the the starting monitor. The scrollMaster code does not need to be touched. You define all the other monitors/computers in 'scrollNodes'. Each number corresponds to a computer id, and the order these are put in the file determines the order the monitors will go in. 'scrollFile' is where you define the text that scrolls. For each line you define a speed (the smaller the number, the faster) with an INTEGER (no decimals), and then type the text that displays on the next line. All the other computers are called 'Slave' computers. They only have one file, 'scrollSlave'. SImply define the computer id of the master computer in that file, and you're good to go. The master and all the slave computers should have a modem, and one monitor (any size) attached to them.

To run, run all of the slave computers FIRST, and then run the master program. If the master program is started first, you will need to restart it once all the slaves are running.

Scroll Master:

Spoiler

--[[
  Scroll Program: Master

  By: Negeator

  Instructions:
  Don't touch this file! (unless you know what you are doing)
  Define all of the slave computer ids in the 'scrollNodes' file.
	Put an id on each line, this will be the order they go in.
  Define all of the lines of text in the 'scrollFile' file.
	Put the speed (higher the number, slower it scrolls, NO DECIMAL VALUES) on one line, and the text on the other.
	In case you can't count, there should be two lines in the text file for every line it displays.
  
  To Run, run all of the slaves first, then run the master program.  If you run the master before one of the slaves, you'll have to stop the master and re-run it.
]]--
sleep(1)  --Make sure slave computers boot first (for logging into a server with computers set to automatically run the program)
local mon
local timeMax
--Arrays Initialized
local nodes = { } --Slave Computer ID's
local text = { }  --Current Text for each line of each monitor
local heights = { }  --Heights of each monitor
local serverText = { }  --Current server text (Feeds one character of this at a time into the Master monitor, and then removes that character.  When the string is empty, it is replenished by being reset using storeServerText)
local storeServerText = { }  --Stored Server Text (this does not change)
local scrollSpeeds = { }  --Time it takes to scroll (1 = fastest, only whole numbers for now)
--[[ Functions ]]--
function main()
--Initialization
getNodeData()  --Load Nodes (slave computer id's) from a file
getMessageData() --Load Message (text and speeds for each line) from a file
startModem()  --Start Modem
startMon()  --Start Monitor
textInit()  --Initialize the Text arrays
getServerText()  --Initialize serverText values
timeMax = getLongestTime()  --Get longest scroll time
local currTime = 0  --Initialize the current time variable
os.startTimer(.01)  --Start the timer (currTime will increment ever .01 seconds)
print("Working!")
while true do
	drawText()
event = os.pullEvent()
if event == "timer" then
   for i = 1, #scrollSpeeds do
	 if (currTime % scrollSpeeds[i]) == 0 then
	   updateCharLine(i)
	   updateText()
	 end
   end
  
   currTime = currTime + 1
   if currTime > timeMax then
	 currTime = 0
   end
  
   os.startTimer(.01)
end
end
end
function getServerText()
  for i = 1, #storeServerText do
	serverText[i] = storeServerText[i]
  end
end
--Update all of the slaves' text
function updateText()
  for i = 1, #nodes do
	sendText(i)
  end
end
--Update text (rows) for a slave
function sendText(index)
  local sendID = nodes[index]
  rednet.send(sendID,"update")
  for i = 1, heights[index + 1] do
	if i > #storeServerText then
   rednet.send(sendID," ")
else
	  rednet.send(sendID,text[index + 1][i])
end
  end
end
--Initialize 'text' (make all of the rows the appropriate length, etc)
function textInit()
  --Add to heights and text for the first monitor (Master)
  local length, height = mon.getSize()
  table.insert(heights,height)
  table.insert(text, { })
  for a = 1, #storeServerText do
	table.insert(text[1],textCreate(length))
  end
print("a")
  --Add to heights and text for the rest of the monitors (Slaves)
  for i = 1, #nodes do
	length = getLength(nodes[i])
	height = getHeight(nodes[i])
	--Create Slave Height
	table.insert(heights,height)
	--Initialize text (rows) for Slaves
	table.insert(text,  { })
	for a = 1, #storeServerText do
	  table.insert(text[#text],textCreate(length))
	end
  end
end
--Create a blank string based on a specific length
function textCreate(len)
local ret = ""
for i = 1, len do
	ret = ret .. " "
end
return ret
end
function updateCharLine(index)
  text[1][index] = text[1][index] .. string.sub(serverText[index],1,1)
  for i = 2, #text do
	text[i][index] = text[i][index] .. string.sub(text[i - 1][index],1,1)
  end

  for i = 1, #text do
	text[i][index] = string.sub(text[i][index],2,string.len(text[i][index]))
  end

  serverText[index] = string.sub(serverText[index], 2, string.len(serverText[index]))
  if serverText[index] == "" then
	serverText[index] = storeServerText[index]
  end
end
--Draw text on the Master's monitor
function drawText()
  for i = 1, #text[1] do
	if i > heights[1] then
   return
end
	mon.setCursorPos(1,i)
	mon.write(text[1][i])
  end
end
--Get the length of a slave's monitor
function getLength(index)
-- term.write(1,1,"Waiting on ".. index .." to send")
  print("Waiting on ".. index)
  rednet.send(index,"length")
  while true do
	id, message = rednet.receive()
if id == index then
   return tonumber(message)
end
  end
end
--Get the height of a slave's monitor
function getHeight(index)
  rednet.send(index,"height")
  while true do
	id, message = rednet.receive()
if id == index then
   return tonumber(message)
end
  end
end
--Start the monitor (detect side, etc)
function startMon()
  local monSide
  for i=1,#rs.getSides() do
	if peripheral.isPresent(rs.getSides()[i]) and peripheral.getType(rs.getSides()[i]) == "monitor" then
   monSide = rs.getSides()[i]
   mon = peripheral.wrap( monSide )
   return
end
  end
  error()
end
--Start the monitor (detect side, etc)
function startModem()
  local modemSide
  for i=1,#rs.getSides() do
	if peripheral.isPresent(rs.getSides()[i]) and peripheral.getType(rs.getSides()[i]) == "modem" then
	  modemSide = rs.getSides()[i]
	  rednet.open(modemSide)
	  return
	end
  end
-- error()
end
--Gets the scroll time that is the longest
function getLongestTime()
  local max = scrollSpeeds[1]
  for i = 2, #scrollSpeeds do
	if scrollSpeeds[i] > max then
	  max = scrollSpeeds[i]
	end
  end
  return max
end
--Load messages and speeds from a file
function getMessageData()
  local file, error = io.open("scrollFile", "r")
  if error then
	return error
  end

  local readText
  local boolean = true
  local num = 0

  while boolean do
	readText = file:read()
if readText ~= nil then
	  if num == 0 then
	 table.insert(scrollSpeeds,tonumber(readText))
  num = 1
   else
	 table.insert(serverText,"")
  table.insert(storeServerText,readText)
  print("  Inserting "..readText)
	 num = 0
   end
else
   boolean = false
end
  end
  file:close()
end
function getNodeData()
  local file, error = io.open("scrollNodes", "r")
  if error then
	return error
  end

  local readText
  local boolean = true
  local num = 0

  while boolean do
	readText = file:read()
if readText ~= nil then
	  table.insert(nodes,tonumber(readText))
else
   boolean = false
end
  end
  file:close()
end

--[[ Run Program ]]--
main()

Scroll File (Scroll Master Computer):

Spoiler

2
Hello World!  This is some text ...
4
Here is another line, scrolling at another speed. The larger the number is set for this line in 'scrollFile', the slower this line will scroll...
2
Be sure to put a space or two at the end of every line.  If you don't there won't be any spaces in between the first word and the last word when this text repeats.
10
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50

Scroll Nodes (Scroll Master Computer):

Spoiler

2
3
4

Scroll Slave:

Spoiler

--[[
  Scroll Program: Slave

  By: Negeator

  Instructions:
  Set the 'serverID' variable to the id of the master computer.

]]--

local serverID = 1  -- CHANGE THIS TO THE COMPUTER ID OF THE MASTER
local mon
local text = { }
local length, height

--[[ Functions ]]--

function main()
--Initialization
startModem()
startMon()

length, height = mon.getSize()
   for i = 1, height do
	 table.insert(text,"")
   end

print("Ready!")
while true do
   drawText()
   event, id, message = os.pullEvent()
   if event == "rednet_message" then
	 if id == serverID then

  if message == "length" then
	rednet.send(serverID,tostring(length))
  elseif message == "height" then
	rednet.send(serverID, tostring(height))

  elseif message == "update" then
	for i = 1, height do
  repeat
	id2, message2 = rednet.receive()
  until id2 == id
  text[i] = message2

end
  end

end
   end

end

end

--Draw Text
function drawText()
  for i = 1, height do
	mon.setCursorPos(1,i)
	mon.write(text[i])
  end
end


--Start the monitor (detect side, etc)
function startMon()
  local monSide
  for i=1,#rs.getSides() do
	if peripheral.isPresent(rs.getSides()[i]) and peripheral.getType(rs.getSides()[i]) == "monitor" then
monSide = rs.getSides()[i]
mon = peripheral.wrap( monSide )
return
end
  end
  error()
end

function startModem()
  local modemSide
  for i=1,#rs.getSides() do
	if peripheral.isPresent(rs.getSides()[i]) and peripheral.getType(rs.getSides()[i]) == "modem" then
	  modemSide = rs.getSides()[i]
	  rednet.open(modemSide)
	  return
	end
  end
  error()
end

--[[ Run Program ]]--
main()