-- Web Bridge Dashboard for Turtle System -- Beautiful visual interface for 2x3 monitor setup -- Forwards turtle status updates to the web server local SERVER_URL = "http://10.10.10.6:4200" -- Change to your server address local CHANNEL_RECEIVE = 101 local STATUS_CHANNEL = 102 local COMMAND_CHANNEL = 100 -- Find peripherals local modem = peripheral.find("modem") local monitor = peripheral.find("monitor") if not modem then error("No wireless modem found!") end -- Check if we have a monitor for dashboard local hasMonitor = monitor ~= nil if hasMonitor then monitor.setTextScale(0.5) end modem.open(CHANNEL_RECEIVE) modem.open(STATUS_CHANNEL) -- Track turtles and stats local turtles = {} local stats = { messagesReceived = 0, commandsSent = 0, serverUpdates = 0, errors = 0, startTime = os.epoch("utc") } local activityLog = {} -- Dashboard drawing functions (only if monitor available) local function centerText(y, text, fg, bg) if not hasMonitor then return end local w = monitor.getSize() monitor.setCursorPos(math.floor((w - #text) / 2) + 1, y) monitor.setTextColor(fg or colors.white) monitor.setBackgroundColor(bg or colors.black) monitor.write(text) end local function drawBox(x, y, width, height, color) if not hasMonitor then return end monitor.setBackgroundColor(color) for dy = 0, height - 1 do monitor.setCursorPos(x, y + dy) monitor.write(string.rep(" ", width)) end end local function formatTime(ms) local seconds = math.floor(ms / 1000) local minutes = math.floor(seconds / 60) local hours = math.floor(minutes / 60) if hours > 0 then return string.format("%dh %dm", hours, minutes % 60) elseif minutes > 0 then return string.format("%dm %ds", minutes, seconds % 60) else return string.format("%ds", seconds) end end local function getStatusColor(turtle) if not turtle.lastSeen then return colors.red end local timeSince = os.epoch("utc") - turtle.lastSeen if timeSince < 10000 then return colors.lime elseif timeSince < 30000 then return colors.yellow else return colors.red end end local function drawDashboard() if not hasMonitor then return end local w, h = monitor.getSize() monitor.setBackgroundColor(colors.black) monitor.clear() -- Header (larger for vertical display) drawBox(1, 1, w, 4, colors.blue) centerText(2, "╔════════════════════════════════════════════════╗", colors.white, colors.blue) centerText(3, "║ TURTLE NETWORK BRIDGE CONTROL CENTER ║", colors.white, colors.blue) centerText(4, "╚════════════════════════════════════════════════╝", colors.white, colors.blue) monitor.setCursorPos(2, 2) monitor.setTextColor(colors.lime) monitor.setBackgroundColor(colors.blue) monitor.write("\7 ONLINE") -- Stats box (left side, taller) local statsY = 6 drawBox(2, statsY, 50, 20, colors.gray) drawBox(3, statsY + 1, 48, 18, colors.black) monitor.setTextColor(colors.yellow) monitor.setBackgroundColor(colors.black) monitor.setCursorPos(4, statsY + 1) monitor.write("╔═══ SYSTEM STATISTICS ═══╗") local statY = statsY + 3 -- Messages monitor.setTextColor(colors.lightGray) monitor.setCursorPos(5, statY) monitor.write("Messages Received:") monitor.setTextColor(colors.white) monitor.setCursorPos(38, statY) monitor.write(tostring(stats.messagesReceived)) statY = statY + 2 -- Commands monitor.setTextColor(colors.lightGray) monitor.setCursorPos(5, statY) monitor.write("Commands Sent:") monitor.setTextColor(colors.lime) monitor.setCursorPos(38, statY) monitor.write(tostring(stats.commandsSent)) statY = statY + 2 -- Server Updates monitor.setTextColor(colors.lightGray) monitor.setCursorPos(5, statY) monitor.write("Server Updates:") monitor.setTextColor(colors.lightBlue) monitor.setCursorPos(38, statY) monitor.write(tostring(stats.serverUpdates)) statY = statY + 2 -- Errors monitor.setTextColor(colors.lightGray) monitor.setCursorPos(5, statY) monitor.write("Errors:") monitor.setTextColor(stats.errors > 0 and colors.red or colors.lime) monitor.setCursorPos(38, statY) monitor.write(tostring(stats.errors)) statY = statY + 2 -- Uptime monitor.setTextColor(colors.lightGray) monitor.setCursorPos(5, statY) monitor.write("Uptime:") monitor.setTextColor(colors.white) monitor.setCursorPos(38, statY) monitor.write(formatTime(os.epoch("utc") - stats.startTime)) statY = statY + 3 -- Connection info monitor.setTextColor(colors.gray) monitor.setCursorPos(5, statY) monitor.write("Server: " .. SERVER_URL:sub(8)) statY = statY + 1 monitor.setCursorPos(5, statY) monitor.write("Channels: " .. STATUS_CHANNEL .. "/" .. COMMAND_CHANNEL) -- Turtle list (right side, much taller) local turtleX = 54 local turtleWidth = w - turtleX - 1 drawBox(turtleX, statsY, turtleWidth, 20, colors.gray) drawBox(turtleX + 1, statsY + 1, turtleWidth - 2, 18, colors.black) monitor.setTextColor(colors.yellow) monitor.setCursorPos(turtleX + 2, statsY + 1) monitor.write("╔═══ ACTIVE TURTLES ═══╗") local turtleY = statsY + 3 local count = 0 for id, turtle in pairs(turtles) do if count >= 15 then break end -- Show up to 15 turtles local statusColor = getStatusColor(turtle) monitor.setCursorPos(turtleX + 2, turtleY) monitor.setTextColor(statusColor) monitor.write("\7") monitor.setTextColor(colors.white) monitor.write(string.format(" T#%-2d", id)) monitor.setCursorPos(turtleX + 12, turtleY) monitor.setTextColor(colors.lightGray) if turtle.state then local state = turtle.state:upper() if #state > 10 then state = state:sub(1, 10) end monitor.write(state) else monitor.write("UNKNOWN") end -- Show position if available if turtle.position then turtleY = turtleY + 1 monitor.setCursorPos(turtleX + 4, turtleY) monitor.setTextColor(colors.gray) monitor.write(string.format("X:%d Y:%d Z:%d", turtle.position.x or 0, turtle.position.y or 0, turtle.position.z or 0)) end turtleY = turtleY + 2 count = count + 1 end if count == 0 then monitor.setCursorPos(turtleX + 2, statsY + 5) monitor.setTextColor(colors.gray) monitor.write("No turtles detected") monitor.setCursorPos(turtleX + 2, statsY + 6) monitor.write("Waiting for signals...") end -- Activity log (bottom section, MUCH taller for vertical display) local logY = 28 drawBox(2, logY, w - 2, h - logY - 1, colors.gray) drawBox(3, logY + 1, w - 4, h - logY - 3, colors.black) monitor.setTextColor(colors.yellow) monitor.setCursorPos(4, logY + 1) monitor.write("╔═══ ACTIVITY LOG ═══╗") -- Calculate how many log lines we can show (huge vertical space!) local maxLines = h - logY - 4 for i = 1, math.min(#activityLog, maxLines) do local entry = activityLog[i] monitor.setCursorPos(4, logY + 2 + i) monitor.setBackgroundColor(colors.black) monitor.setTextColor(colors.gray) local time = os.date("%H:%M:%S", entry.time / 1000) monitor.write("[" .. time .. "] ") monitor.setTextColor(entry.color) -- Truncate long messages to fit width local maxTextWidth = w - 18 local text = entry.text if #text > maxTextWidth then text = text:sub(1, maxTextWidth - 3) .. "..." end monitor.write(text) end -- Footer monitor.setBackgroundColor(colors.gray) monitor.setCursorPos(1, h) monitor.clearLine() centerText(h, "Ctrl+T to terminate | Monitoring " .. STATUS_CHANNEL .. " & " .. COMMAND_CHANNEL, colors.white, colors.gray) end local function addLog(text, color) table.insert(activityLog, 1, {text = text, color = color or colors.white, time = os.epoch("utc")}) if #activityLog > 35 then table.remove(activityLog) end -- Also print to console if no monitor if not hasMonitor then print(text) end end -- Function to send data to web server local function sendToServer(data) local success, result = pcall(function() local jsonData = textutils.serializeJSON(data) local response = http.post( SERVER_URL .. "/api/turtle/update", jsonData, {["Content-Type"] = "application/json"} ) if response then response.close() return true else return false end end) return success and result end -- Function to poll for commands from server local function pollCommands(turtleID) local success, result = pcall(function() local response = http.get(SERVER_URL .. "/api/turtle/" .. turtleID .. "/commands") if response then local content = response.readAll() response.close() local data = textutils.unserializeJSON(content) if data and data.commands then return data.commands end end return {} end) if success then return result else stats.errors = stats.errors + 1 return {} end end -- Initial setup if hasMonitor then drawDashboard() addLog("System initialized with monitor", colors.lime) else print("Web Bridge Started (No monitor)") print("Listening for turtle updates...") print("Server: " .. SERVER_URL) end addLog("Listening on channels " .. STATUS_CHANNEL .. " and " .. CHANNEL_RECEIVE, colors.lightBlue) -- Start polling timer local POLL_INTERVAL = 1 -- Poll every 1 second os.startTimer(POLL_INTERVAL) -- Main loop local lastRefresh = os.epoch("utc") while true do local event, side, channel, replyChannel, message, distance = os.pullEvent() if event == "timer" then -- Poll for commands for all known turtles for turtleID, turtleData in pairs(turtles) do local commands = pollCommands(turtleID) -- Forward commands back to turtle for _, cmd in ipairs(commands) do stats.commandsSent = stats.commandsSent + 1 addLog("CMD: " .. cmd.command .. " -> Turtle #" .. turtleID, colors.yellow) local commandPacket = { command = cmd.command, param = cmd.param, target = turtleID } modem.transmit(COMMAND_CHANNEL, CHANNEL_RECEIVE, commandPacket) -- Debug: show what we're sending if not hasMonitor then print(" Sent to channel " .. COMMAND_CHANNEL .. ": target=" .. turtleID) end end end -- Restart timer os.startTimer(POLL_INTERVAL) -- Refresh display if we have a monitor if hasMonitor then local now = os.epoch("utc") if now - lastRefresh > 2000 then drawDashboard() lastRefresh = now end end elseif event == "modem_message" then -- Only process messages on our channels if channel == STATUS_CHANNEL or channel == CHANNEL_RECEIVE then stats.messagesReceived = stats.messagesReceived + 1 if type(message) == "table" then if message.type == "status" then local turtleID = message.turtleID -- Update turtle data turtles[turtleID] = message turtles[turtleID].lastSeen = os.epoch("utc") addLog("Turtle #" .. turtleID .. " - " .. (message.state or "status"), colors.lightBlue) -- Forward to web server local success = sendToServer(message) if success then stats.serverUpdates = stats.serverUpdates + 1 addLog(" -> Forwarded to server", colors.lime) else stats.errors = stats.errors + 1 addLog(" -> Server error", colors.red) end end end end end end